diff options
author | Dan Ungureanu <udan1107@gmail.com> | 2015-05-26 01:02:55 +0300 |
---|---|---|
committer | Dan Ungureanu <udan1107@gmail.com> | 2015-06-08 19:37:46 +0300 |
commit | 0a52978705d59c50f785f0f9cf537161046beb21 (patch) | |
tree | 00051de2f829f6b1f5c95c54da89be1f298b77a8 | |
download | sql-parser-0a52978705d59c50f785f0f9cf537161046beb21.zip sql-parser-0a52978705d59c50f785f0f9cf537161046beb21.tar.gz sql-parser-0a52978705d59c50f785f0f9cf537161046beb21.tar.bz2 |
Initial commit.
128 files changed, 6833 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fc2819 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor/ +coverage.xml diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..40b0e9d --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# SQL Parser + +A validating SQL lexer and parser with a focus on MySQL dialect. + +This library was originally developed for phpMyAdmin during the Google Summer of Code 2015. + +This is an alpha version. For more information, please check: [Overview](https://github.com/udan11/sql-parser/wiki/Overview) or the [Examples](https://github.com/udan11/sql-parser/wiki/Examples).
\ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..efe0e56 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "udan11/sql-parser", + "description": "A validating SQL lexer and parser with a focus on MySQL dialect.", + "license": "GPLv2", + "keywords": ["sql", "lexer", "parser", "analysis"], + "homepage": "https://github.com/udan11/sql-parser", + "support": { + "issues": "https://github.com/udan11/sql-parser/issues", + "source": "https://github.com/udan11/sql-parser" + }, + "authors": [ + { + "name": "Dan Ungureanu", + "email": "udan1107@gmail.com" + } + ], + "require": {}, + "require-dev": { + "phpunit/php-code-coverage": "~2.0", + "phpunit/phpunit": "4.*" + }, + "autoload": { + "psr-4": { + "SqlParser\\": "src" + } + } +}
\ No newline at end of file diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..eb408bd --- /dev/null +++ b/composer.lock @@ -0,0 +1,1048 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "6699f231f94d0f83909de340d98260dc", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "2.0.*@ALPHA" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Instantiator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2014-10-13 12:58:55" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpspec/prophecy", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", + "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2015-04-27 22:15:08" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "3703c4bb67c8700957dd41c843254658539d091d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/3703c4bb67c8700957dd41c843254658539d091d", + "reference": "3703c4bb67c8700957dd41c843254658539d091d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "~1.0", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-06-06 08:33:23" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb", + "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2015-04-02 05:19:05" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2014-01-30 17:20:04" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "eab81d02569310739373308137284e0158424330" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/eab81d02569310739373308137284e0158424330", + "reference": "eab81d02569310739373308137284e0158424330", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2015-04-08 04:46:07" + }, + { + "name": "phpunit/phpunit", + "version": "4.7.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "8e0c63329c8c4185296b8d357daa5c6bae43080f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e0c63329c8c4185296b8d357daa5c6bae43080f", + "reference": "8e0c63329c8c4185296b8d357daa5c6bae43080f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "~1.3,>=1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.2", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2015-06-06 08:36:08" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "253c005852591fd547fc18cd5b7b43a1ec82d8f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/253c005852591fd547fc18cd5b7b43a1ec82d8f7", + "reference": "253c005852591fd547fc18cd5b7b43a1ec82d8f7", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "~1.0,>=1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-05-29 05:19:18" + }, + { + "name": "sebastian/comparator", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-01-29 16:28:08" + }, + { + "name": "sebastian/diff", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", + "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-02-22 15:13:53" + }, + { + "name": "sebastian/environment", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5a8c7d31914337b69923db26c4221b81ff5a196e", + "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2015-01-01 10:01:08" + }, + { + "name": "sebastian/exporter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "84839970d05254c73cde183a721c7af13aede943" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", + "reference": "84839970d05254c73cde183a721c7af13aede943", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2015-01-27 07:23:06" + }, + { + "name": "sebastian/global-state", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2014-10-06 09:23:50" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-01-24 09:48:32" + }, + { + "name": "sebastian/version", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", + "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-02-24 06:35:25" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "e96d8579fbed0c95ecf2a0501ec4f307a4aa6404" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/e96d8579fbed0c95ecf2a0501ec4f307a4aa6404", + "reference": "e96d8579fbed0c95ecf2a0501ec4f307a4aa6404", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.1.2" + }, + "bin": [ + "scripts/phpcs", + "scripts/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "CodeSniffer.php", + "CodeSniffer/CLI.php", + "CodeSniffer/Exception.php", + "CodeSniffer/File.php", + "CodeSniffer/Fixer.php", + "CodeSniffer/Report.php", + "CodeSniffer/Reporting.php", + "CodeSniffer/Sniff.php", + "CodeSniffer/Tokens.php", + "CodeSniffer/Reports/", + "CodeSniffer/Tokenizers/", + "CodeSniffer/DocGenerators/", + "CodeSniffer/Standards/AbstractPatternSniff.php", + "CodeSniffer/Standards/AbstractScopeSniff.php", + "CodeSniffer/Standards/AbstractVariableSniff.php", + "CodeSniffer/Standards/IncorrectPatternException.php", + "CodeSniffer/Standards/Generic/Sniffs/", + "CodeSniffer/Standards/MySource/Sniffs/", + "CodeSniffer/Standards/PEAR/Sniffs/", + "CodeSniffer/Standards/PSR1/Sniffs/", + "CodeSniffer/Standards/PSR2/Sniffs/", + "CodeSniffer/Standards/Squiz/Sniffs/", + "CodeSniffer/Standards/Zend/Sniffs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "http://www.squizlabs.com/php-codesniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2015-04-28 23:28:20" + }, + { + "name": "symfony/yaml", + "version": "v2.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "4a29a5248aed4fb45f626a7bbbd330291492f5c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/4a29a5248aed4fb45f626a7bbbd330291492f5c3", + "reference": "4a29a5248aed4fb45f626a7bbbd330291492f5c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2015-05-02 15:21:08" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..818fccd --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit backupGlobals="false" + backupStaticAttributes="false" + bootstrap="tests/bootstrap.php" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false"> + <logging> + <log type="coverage-clover" target="coverage.xml" /> + </logging> + <testsuites> + <testsuite name="Lexer's Test Suite"> + <directory suffix=".php">./tests/lexer</directory> + </testsuite> + <testsuite name="Parser's Test Suite"> + <directory suffix=".php">./tests/parser</directory> + </testsuite> + </testsuites> +</phpunit>
\ No newline at end of file diff --git a/src/Context.php b/src/Context.php new file mode 100644 index 0000000..8c2ac14 --- /dev/null +++ b/src/Context.php @@ -0,0 +1,470 @@ +<?php + +namespace SqlParser; + +/** + * Default MySQL context (based on MySQL 5.7). + */ +abstract class Context +{ + + /** + * The maximum length of a keyword. + * + * @see static::$TOKEN_KEYWORD + * + * @var int + */ + const KEYWORD_MAX_LENGTH = 30; + + /** + * The maximum length of an operator. + * + * @see static::$TOKEN_OPERATOR + * + * @var int + */ + const OPERATOR_MAX_LENGTH = 4; + + + // ------------------------------------------------------------------------- + // Keywords. + + /** + * List of official keywords. + * + * Because, PHP's associative arrays are basically hash tables, it is more + * efficient to store keywords as keys instead of values. + * + * @var array + */ + public static $KEYWORDS = array( + 'AS' => 1, 'BY' => 1, 'IF' => 1, 'IN' => 1, 'IS' => 1, 'ON' => 1, + 'OR' => 1, 'TO' => 1, + 'ADD' => 1, 'ALL' => 1, 'AND' => 1, 'ASC' => 1, 'DEC' => 1, 'DIV' => 1, + 'FOR' => 1, 'GET' => 1, 'INT' => 1, 'KEY' => 1, 'MOD' => 1, 'NOT' => 1, + 'OUT' => 1, 'SET' => 1, 'SQL' => 1, 'SSL' => 1, 'USE' => 1, 'XOR' => 1, + 'BLOB' => 1, 'BOTH' => 1, 'CALL' => 1, 'CASE' => 1, 'CHAR' => 1, + 'DESC' => 1, 'DROP' => 1, 'DUAL' => 1, 'EACH' => 1, 'ELSE' => 1, + 'EXIT' => 1, 'FROM' => 1, 'GOTO' => 1, 'INT1' => 1, 'INT2' => 1, + 'INT3' => 1, 'INT4' => 1, 'INT8' => 1, 'INTO' => 1, 'JOIN' => 1, + 'KEYS' => 1, 'KILL' => 1, 'LEFT' => 1, 'LIKE' => 1, 'LOAD' => 1, + 'LOCK' => 1, 'LONG' => 1, 'LOOP' => 1, 'NULL' => 1, 'READ' => 1, + 'REAL' => 1, 'SHOW' => 1, 'THEN' => 1, 'TRUE' => 1, 'UNDO' => 1, + 'WHEN' => 1, 'WITH' => 1, + 'ALTER' => 1, 'CHECK' => 1, 'CROSS' => 1, 'FALSE' => 1, 'FETCH' => 1, + 'FLOAT' => 1, 'FORCE' => 1, 'GRANT' => 1, 'GROUP' => 1, 'INDEX' => 1, + 'INNER' => 1, 'INOUT' => 1, 'LABEL' => 1, 'LEAVE' => 1, 'LIMIT' => 1, + 'LINES' => 1, 'MATCH' => 1, 'ORDER' => 1, 'OUTER' => 1, 'PURGE' => 1, + 'RANGE' => 1, 'READS' => 1, 'RIGHT' => 1, 'RLIKE' => 1, 'TABLE' => 1, + 'UNION' => 1, 'USAGE' => 1, 'USING' => 1, 'WHERE' => 1, 'WHILE' => 1, + 'WRITE' => 1, + 'BEFORE' => 1, 'BIGINT' => 1, 'BINARY' => 1, 'CHANGE' => 1, + 'COLUMN' => 1, 'CREATE' => 1, 'CURSOR' => 1, 'DELETE' => 1, + 'DOUBLE' => 1, 'ELSEIF' => 1, 'EXISTS' => 1, 'FLOAT4' => 1, + 'FLOAT8' => 1, 'HAVING' => 1, 'IGNORE' => 1, 'INFILE' => 1, + 'INSERT' => 1, 'LINEAR' => 1, 'OPTION' => 1, 'REGEXP' => 1, + 'RENAME' => 1, 'REPEAT' => 1, 'RETURN' => 1, 'REVOKE' => 1, + 'SCHEMA' => 1, 'SELECT' => 1, 'SIGNAL' => 1, 'SONAME' => 1, + 'UNIQUE' => 1, 'UNLOCK' => 1, 'UPDATE' => 1, 'VALUES' => 1, + 'ANALYZE' => 1, 'BETWEEN' => 1, 'CASCADE' => 1, 'COLLATE' => 1, + 'CONVERT' => 1, 'DECIMAL' => 1, 'DECLARE' => 1, 'DEFAULT' => 1, + 'DELAYED' => 1, 'ESCAPED' => 1, 'EXPLAIN' => 1, 'FOREIGN' => 1, + 'INTEGER' => 1, 'ITERATE' => 1, 'LEADING' => 1, 'NATURAL' => 1, + 'NUMERIC' => 1, 'OUTFILE' => 1, 'PRIMARY' => 1, 'RELEASE' => 1, + 'REPLACE' => 1, 'REQUIRE' => 1, 'SCHEMAS' => 1, 'SPATIAL' => 1, + 'TINYINT' => 1, 'TRIGGER' => 1, 'UPGRADE' => 1, 'VARCHAR' => 1, + 'VARYING' => 1, + 'CONTINUE' => 1, 'DATABASE' => 1, 'DAY_HOUR' => 1, 'DESCRIBE' => 1, + 'DISTINCT' => 1, 'ENCLOSED' => 1, 'FULLTEXT' => 1, 'INTERVAL' => 1, + 'LONGBLOB' => 1, 'LONGTEXT' => 1, 'MAXVALUE' => 1, 'MODIFIES' => 1, + 'OPTIMIZE' => 1, 'RESIGNAL' => 1, 'RESTRICT' => 1, 'SMALLINT' => 1, + 'SPECIFIC' => 1, 'SQLSTATE' => 1, 'STARTING' => 1, 'TINYBLOB' => 1, + 'TINYTEXT' => 1, 'TRAILING' => 1, 'UNSIGNED' => 1, 'UTC_DATE' => 1, + 'UTC_TIME' => 1, 'ZEROFILL' => 1, + 'CHARACTER' => 1, 'CONDITION' => 1, 'DATABASES' => 1, 'LOCALTIME' => 1, + 'MEDIUMINT' => 1, 'MIDDLEINT' => 1, 'PARTITION' => 1, 'PRECISION' => 1, + 'PROCEDURE' => 1, 'SENSITIVE' => 1, 'SEPARATOR' => 1, 'VARBINARY' => 1, + 'ACCESSIBLE' => 1, 'ASENSITIVE' => 1, 'CONNECTION' => 1, + 'CONSTRAINT' => 1, 'DAY_MINUTE' => 1, 'DAY_SECOND' => 1, + 'MEDIUMBLOB' => 1, 'MEDIUMTEXT' => 1, 'OPTIONALLY' => 1, + 'READ_WRITE' => 1, 'REFERENCES' => 1, 'SQLWARNING' => 1, + 'TERMINATED' => 1, 'YEAR_MONTH' => 1, + 'DISTINCTROW' => 1, 'HOUR_MINUTE' => 1, 'HOUR_SECOND' => 1, + 'INSENSITIVE' => 1, 'MASTER_BIND' => 1, + 'CURRENT_DATE' => 1, 'CURRENT_TIME' => 1, 'CURRENT_USER' => 1, + 'LOW_PRIORITY' => 1, 'SQLEXCEPTION' => 1, 'VARCHARACTER' => 1, + 'DETERMINISTIC' => 1, 'HIGH_PRIORITY' => 1, 'MINUTE_SECOND' => 1, + 'STRAIGHT_JOIN' => 1, 'UTC_TIMESTAMP' => 1, + 'IO_AFTER_GTIDS' => 1, 'LOCALTIMESTAMP' => 1, 'SQL_BIG_RESULT' => 1, + 'DAY_MICROSECOND' => 1, 'IO_BEFORE_GTIDS' => 1, 'OPTIMIZER_COSTS' => 1, + 'HOUR_MICROSECOND' => 1, 'SQL_SMALL_RESULT' => 1, + 'CURRENT_TIMESTAMP' => 1, + 'MINUTE_MICROSECOND' => 1, 'NO_WRITE_TO_BINLOG' => 1, + 'SECOND_MICROSECOND' => 1, + 'SQL_CALC_FOUND_ROWS' => 1, + 'MASTER_SSL_VERIFY_SERVER_CERT' => 1, + + /* + * Secondary group of keywords. + * + * Keywords below are either words that are used as keywords, but not + * defined as proper keywords or are group of keywords which are parsed + * easier when found grouped. + */ + + 'END' => 2, + 'INDEX' => 2, 'VALUE' => 2, + 'ENGINE' => 2, + 'COMMENT' => 2, 'RETURNS' => 2, 'STORAGE' => 2, + 'CHECKSUM' => 2, 'MAX_ROWS' => 2, 'MIN_ROWS' => 2, 'NOT NULL' => 2, + 'PASSWORD' => 2, + 'INDEX KEY' => 2, 'PACK_KEYS' => 2, + 'ROW_FORMAT' => 2, 'TABLESPACE' => 2, + 'UNIQUE KEY' => 2, + 'FOREIGN KEY' => 2, 'PRIMARY KEY' => 2, 'PRIMARY KEY' => 2, + 'SPATIAL KEY' => 2, + 'FULLTEXT KEY' => 2, 'UNIQUE INDEX' => 2, + 'CHARACTER SET' => 2, 'IF NOT EXISTS' => 2, 'INSERT_METHOD' => 2, + 'SPATIAL INDEX' => 2, + 'AUTO_INCREMENT' => 2, 'AVG_ROW_LENGTH' => 2, 'DATA DIRECTORY' => 2, + 'FULLTEXT INDEX' => 2, 'KEY_BLOCK_SIZE' => 2, + 'DEFAULT COLLATE' => 2, + 'DELAY_KEY_WRITE' => 2, 'INDEX DIRECTORY' => 2, + 'DEFAULT CHARACTER SET' => 2, + ); + + // ------------------------------------------------------------------------- + // Keys and Data Types. + + /** + * Types of keys. + * + * @var array + */ + public static $KEY_TYPES = array( + 'FOREIGN KEY' => 1, 'FULLTEXT INDEX' => 1, 'FULLTEXT KEY' => 1, + 'INDEX KEY' => 1, 'INDEX' => 1, 'KEY' => 1, 'PRIMARY KEY' => 1, + 'SPATIAL INDEX' => 1, 'SPATIAL KEY' => 1, 'UNIQUE INDEX' => 1, + 'UNIQUE KEY' => 1, 'UNIQUE' => 1, + ); + + /** + * All data types. + * + * @var array + */ + public static $DATA_TYPES = array( + 'ARRAY' => 1, 'BIGINT' => 1, 'BINARY VARYING' => 1, 'BINARY' => 1, + 'BOOLEAN' => 1, 'CHARACTER' => 1, 'CHARACTER' => 1, 'DATE' => 1, + 'DECIMAL' => 1, 'DOUBLE' => 1, 'FLOAT' => 1, 'FLOAT' => 1, 'INT' => 1, + 'INTEGER' => 1, 'INTERVAL' => 1, 'MULTISET' => 1, 'NUMERIC' => 1, + 'REAL' => 1, 'SMALLINT' => 1, 'TIME' => 1, 'TIMESTAMP' => 1, + 'VARBINARY' => 1, 'VARCHAR' => 1, 'XML' => 1, + ); + + // ------------------------------------------------------------------------- + // Operators. + + /** + * List of operators and their flags. + * + * @var array + */ + public static $OPERATORS = array( + + // Some operators (*, =) may have ambigous flags, because they depend on + // the context they are being used in. + // For example: 1. SELECT * FROM table; # SQL specific (wildcard) + // SELECT 2 * 3; # arithmetic + // 2. SELECT * FROM table WHERE foo = 'bar'; + // SET @i = 0; + + // @see Token::FLAG_OPERATOR_ARITHMETIC + '%' => 1, '*' => 1, '+' => 1, '-' => 1, '/' => 1, + + // @see Token::FLAG_OPERATOR_LOGICAL + '!' => 2, '!==' => 2, '&&' => 2, '<' => 2, '<=' => 2, + '<=>' => 2, '<>' => 2, '=' => 2, '>' => 2, '>=' => 2, + '||' => 2, + + // @see Token::FLAG_OPERATOR_BITWISE + '&' => 4, '<<' => 4, '>>' => 4, '^' => 4, '|' => 4, + '~' => 4, + + // @see Token::FLAG_OPERATOR_ASSIGNMENT + ':=' => 8, + + // @see Token::FLAG_OPERATOR_SQL + '(' => 16, ')' => 16, '.' => 16, ',' => 16, + ); + + // ------------------------------------------------------------------------- + // SQL Modes. + + /* + * Server SQL Modes + * https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html + */ + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_allow_invalid_dates + const ALLOW_INVALID_DATES = 1; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_ansi_quotes + const ANSI_QUOTES = 2; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_error_for_division_by_zero + const ERROR_FOR_DIVISION_BY_ZERO = 4; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_high_not_precedence + const HIGH_NOT_PRECEDENCE = 8; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_ignore_space + const IGNORE_SPACE = 16; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_auto_create_user + const NO_AUTO_CREATE_USER = 32; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_auto_value_on_zero + const NO_AUTO_VALUE_ON_ZERO = 64; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_backslash_escapes + const NO_BACKSLASH_ESCAPES = 128; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_dir_in_create + const NO_DIR_IN_CREATE = 256; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_dir_in_create + const NO_ENGINE_SUBSTITUTION = 512; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_field_options + const NO_FIELD_OPTIONS = 1024; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_key_options + const NO_KEY_OPTIONS = 2048; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_table_options + const NO_TABLE_OPTIONS = 4096; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_unsigned_subtraction + const NO_UNSIGNED_SUBTRACTION = 8192; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_zero_date + const NO_ZERO_DATE = 16384; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_no_zero_in_date + const NO_ZERO_IN_DATE = 32768; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_only_full_group_by + const ONLY_FULL_GROUP_BY = 65536; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_pipes_as_concat + const PIPES_AS_CONCAT = 131072; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_real_as_float + const REAL_AS_FLOAT = 262144; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_strict_all_tables + const STRICT_ALL_TABLES = 524288; + + // https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sqlmode_strict_trans_tables + const STRICT_TRANS_TABLES = 1048576; + + /* + * Combination SQL Modes + * https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sql-mode-combo + */ + + // REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE + const SQL_MODE_ANSI = 393234; + + // PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, + // NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, + const SQL_MODE_DB2 = 138258; + + // PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, + // NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER + const SQL_MODE_MAXDB = 138290; + + // PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, + // NO_TABLE_OPTIONS, NO_FIELD_OPTIONS + const SQL_MODE_MSSQL = 138258; + + // PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, + // NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER + const SQL_MODE_ORACLE = 138290; + + // PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, + // NO_TABLE_OPTIONS, NO_FIELD_OPTIONS + const SQL_MODE_POSTGRESQL = 138258; + + // STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, + // ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER + const SQL_MODE_TRADITIONAL = 1622052; + + // ------------------------------------------------------------------------- + // Keyword. + + /** + * Checks if the given string is a keyword. + * + * @param string $str + * + * @return bool + */ + public static function isKeyword($str) + { + $str = strtoupper($str); + + return isset(static::$KEYWORDS[$str]); + } + + // ------------------------------------------------------------------------- + // Operator. + + /** + * Checks if the given string is an operator. + * + * @param string $str + * + * @return int The appropriate flag for the operator. + */ + public static function isOperator($str) + { + if (!isset(static::$OPERATORS[$str])) { + return null; + } + return static::$OPERATORS[$str]; + } + + // ------------------------------------------------------------------------- + // Whitespace. + + /** + * Checks if the given character is a whitespace. + * + * @param string $ch + * + * @return bool + */ + public static function isWhitespace($ch) + { + return ($ch === ' ') || ($ch === "\r") || ($ch === "\n") || ($ch === "\t"); + } + + // ------------------------------------------------------------------------- + // Comment. + + /** + * Checks if the given string is the beginning of a whitespace. + * + * @param string $str + * + * @return int The appropriate flag for the comment type. + */ + public static function isComment($str) + { + $len = strlen($str); + if ($str[0] === '#') { + return Token::FLAG_COMMENT_BASH; + } elseif (($len > 1) && ((($str[0] === '/') && ($str[1] === '*')) || + (($str[0] === '*') && ($str[1] === '/')))) { + return Token::FLAG_COMMENT_C; + } elseif (($len > 2) && ($str[0] === '-') && + ($str[1] === '-') && ($str[2] !== "\n") && + (static::isWhitespace($str[2]))) { + return Token::FLAG_COMMENT_SQL; + } + return null; + } + + // ------------------------------------------------------------------------- + // Bool. + + /** + * Checks if the given string is a boolean value. + * This actually check only for `TRUE` and `FALSE` because `1` or `0` are + * actually numbers and are parsed by specific methods. + * + * @param string $ch + * + * @return bool + */ + public static function isBool($ch) + { + $ch = strtoupper($ch); + return ($ch === 'TRUE') || ($ch === 'FALSE'); + } + + // ------------------------------------------------------------------------- + // Number. + + /** + * Checks if the given character can be a part of a number. + * + * @param string $ch + * + * @return bool + */ + public static function isNumber($ch) + { + return (($ch >= '0') && ($ch <= '9')) || ($ch === '.') || + ($ch === '-') || ($ch === '+') || ($ch === 'e') || ($ch === 'E'); + } + + // ------------------------------------------------------------------------- + // Symbol. + + /** + * Checks if the given character is the beginning of a symbol. A symbol + * can be either a variable or a field name. + * + * @param string $ch + * + * @return int The appropriate flag for the symbol type. + */ + public static function isSymbol($ch) + { + if ($ch[0] === '@') { + return Token::FLAG_SYMBOL_VARIABLE; + } elseif ($ch[0] === '`') { + return Token::FLAG_SYMBOL_BACKTICK; + } + return null; + } + + // ------------------------------------------------------------------------- + // String. + + /** + * Checks if the given character is the beginning of a string. + * + * @param string $str + * + * @return int The appropriate flag for the string type. + */ + public static function isString($str) + { + if ($str[0] === '\'') { + return Token::FLAG_STRING_SINGLE_QUOTES; + } elseif ($str[0] === '"') { + return Token::FLAG_STRING_DOUBLE_QUOTES; + } + return null; + } + + // ------------------------------------------------------------------------- + // Delimiter. + + /** + * Checks if the given character can be a separator for two lexems. + * + * @param string $ch + * + * @return bool + */ + public static function isSeparator($ch) + { + return !ctype_alnum($ch) && $ch !== '_'; + } +} diff --git a/src/Exceptions/LexerException.php b/src/Exceptions/LexerException.php new file mode 100644 index 0000000..f346d83 --- /dev/null +++ b/src/Exceptions/LexerException.php @@ -0,0 +1,41 @@ +<?php + +namespace SqlParser\Exceptions; + +use SqlParser\Token; + +/** + * Exception thrown by the lexer. + */ +class LexerException extends \Exception +{ + + /** + * The character that produced this error. + * + * @var string + */ + public $ch; + + /** + * The index of the character that produced this error. + * + * @var int + */ + public $pos; + + /** + * Constructor. + * + * @param string $message + * @param string $ch + * @param int $positiion + * @param int $code + */ + public function __construct($message = '', $ch = '', $pos = 0, $code = 0) + { + parent::__construct($message, $code); + $this->ch = $ch; + $this->pos = $pos; + } +} diff --git a/src/Exceptions/ParserException.php b/src/Exceptions/ParserException.php new file mode 100644 index 0000000..e23d03d --- /dev/null +++ b/src/Exceptions/ParserException.php @@ -0,0 +1,32 @@ +<?php + +namespace SqlParser\Exceptions; + +use SqlParser\Token; + +/** + * Exception thrown by the parser. + */ +class ParserException extends \Exception +{ + + /** + * The token that produced this error. + * + * @var Token + */ + public $token; + + /** + * Constructor. + * + * @param string $message + * @param Token $token + * @param int $code + */ + public function __construct($message = '', Token $token = null, $code = 0) + { + parent::__construct($message, $code); + $this->token = $token; + } +} diff --git a/src/Fragment.php b/src/Fragment.php new file mode 100644 index 0000000..e9a4620 --- /dev/null +++ b/src/Fragment.php @@ -0,0 +1,30 @@ +<?php + +namespace SqlParser; + +/** + * A fragment (of a statement) is a part of a statement that is common to + * multiple query types. + */ +abstract class Fragment +{ + + /** + * Array which contains all tokens used to popoluate data inside this + * fragment. + * + * @var array + */ + public $tokens = array(); + + /** + * Parses the tokens given by the lexer in the context of the given parser. + * + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return array + */ + abstract public static function parse(Parser $parser, TokensList $list, array $options = array()); +} diff --git a/src/Fragments/ArrayFragment.php b/src/Fragments/ArrayFragment.php new file mode 100644 index 0000000..9ff8649 --- /dev/null +++ b/src/Fragments/ArrayFragment.php @@ -0,0 +1,95 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses an array. + */ +class ArrayFragment extends Fragment +{ + + /** + * The array. + * + * @var array + */ + public $array = array(); + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return ArrayFragment + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new ArrayFragment(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 -----------------------[ ( ]------------------------> 1 + * + * 1 ------------------[ array element ]-----------------> 2 + * + * 2 ------------------------[ , ]-----------------------> 1 + * 2 ------------------------[ ) ]-----------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($state === 0) { + if (($token->type !== Token::TYPE_OPERATOR) || ($token->value !== '(')) { + $parser->error('An open bracket was expected.', $token); + break; + } + $state = 1; + } elseif ($state === 1) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ')')) { + // Empty array. + break; + } + $ret->array[] = $token->value; + $ret->tokens[] = $token; + $state = 2; + } elseif ($state === 2) { + if (($token->type !== Token::TYPE_OPERATOR) || (($token->value !== ',') && ($token->value !== ')'))) { + $parser->error('Symbols \')\' or \',\' were expected', $token); + break; + } + if ($token->value === ',') { + $state = 1; + } else { // ) + break; + } + } + + } + + return $ret; + } +} diff --git a/src/Fragments/CallKeyword.php b/src/Fragments/CallKeyword.php new file mode 100644 index 0000000..00b7838 --- /dev/null +++ b/src/Fragments/CallKeyword.php @@ -0,0 +1,86 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses a function call. + */ +class CallKeyword extends Fragment +{ + + /** + * The name of this function. + * + * @var string + */ + public $name; + + /** + * The list of parameters + * + * @var array + */ + public $parameters = array(); + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return CallKeyword + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new CallKeyword(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 ----------------------[ name ]-----------------------> 1 + * + * 1 --------------------[ parameters ]-------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($state === 0) { + $ret->name = $token->value; + $ret->tokens[] = $token; + $state = 1; + } elseif ($state === 1) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { + $parameters = ArrayFragment::parse($parser, $list); + $ret->parameters = $parameters->array; + $ret->tokens = array_merge($ret->tokens, $parameters->tokens); + } + break; + } + + } + + return $ret; + } +} diff --git a/src/Fragments/CreateDefFragment.php b/src/Fragments/CreateDefFragment.php new file mode 100644 index 0000000..708ce11 --- /dev/null +++ b/src/Fragments/CreateDefFragment.php @@ -0,0 +1,108 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses the definition that follows the `CREATE` keyword. + */ +class CreateDefFragment extends Fragment +{ + + /** + * All table options. + * + * @var array + */ + public static $TABLE_OPTIONS = array( + 'ENGINE' => array(1, 'var'), + 'AUTO_INCREMENT' => array(2, 'var'), + 'AVG_ROW_LENGTH' => array(3, 'var'), + 'DEFAULT CHARACTER SET' => array(4, 'var'), + 'CHARACTER SET' => array(4, 'var'), + 'CHECKSUM' => array(5, 'var'), + 'DEFAULT COLLATE' => array(5, 'var'), + 'COLLATE' => array(6, 'var'), + 'COMMENT' => array(7, 'var'), + 'CONNECTION' => array(8, 'var'), + 'DATA DIRECTORY' => array(9, 'var'), + 'DELAY_KEY_WRITE' => array(10, 'var'), + 'INDEX DIRECTORY' => array(11, 'var'), + 'INSERT_METHOD' => array(12, 'var'), + 'KEY_BLOCK_SIZE' => array(13, 'var'), + 'MAX_ROWS' => array(14, 'var'), + 'MIN_ROWS' => array(15, 'var'), + 'PACK_KEYS' => array(16, 'var'), + 'PASSWORD' => array(17, 'var'), + 'ROW_FORMAT' => array(18, 'var'), + 'TABLESPACE' => array(19, 'var'), + 'STORAGE' => array(20, 'var'), + 'UNION' => array(21, 'var'), + ); + + /** + * All function options. + * + * @var array + */ + public static $FUNC_OPTIONS = array( + 'COMMENT' => array(1, 'var'), + 'LANGUAGE SQL' => 2, + 'DETERMINISTIC' => 3, + 'NOT DETERMINISTIC' => 3, + 'CONSTAINS SQL' => 4, + 'NO SQL' => 4, + 'READS SQL DATA' => 4, + 'MODIFIES SQL DATA' => 4, + 'SQL SEQURITY DEFINER' => array(5, 'var'), + ); + + /** + * The name of the new table. + * + * @var string + */ + public $name; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return CreateDefFragment + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new CreateDefFragment(); + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { + break; + } + + $ret->tokens[] = $token; + $ret->name .= $token->value; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/DataTypeFragment.php b/src/Fragments/DataTypeFragment.php new file mode 100644 index 0000000..acf3933 --- /dev/null +++ b/src/Fragments/DataTypeFragment.php @@ -0,0 +1,92 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Context; +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `RETURN` keyword parser. + */ +class DataTypeFragment extends Fragment +{ + + /** + * The data type returned. + * + * @var string + */ + public $type; + + /** + * The size of this variable. + * + * @var array + */ + public $size; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return DataTypeFragment[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new DataTypeFragment(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 -------------------[ data type ]--------------------> 1 + * + * 1 ------------------[ size (array) ]------------------> 4 + * 1 ----------------------[ else ]----------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($state === 0) { + $ret->type = $token->value; + $ret->tokens[] = $token; + if (!isset(Context::$DATA_TYPES[$token->value])) { + $parser->error('Unrecognized data type.', $token); + } + $state = 1; + } elseif ($state === 1) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { + $size = ArrayFragment::parse($parser, $list); + $ret->size = $size->array; + $ret->tokens = array_merge($ret->tokens, $size->tokens); + } else { + --$list->idx; + } + break; + } + + } + + if (empty($ret->type)) { + return null; + } + + return $ret; + } +} diff --git a/src/Fragments/FieldDefFragment.php b/src/Fragments/FieldDefFragment.php new file mode 100644 index 0000000..fe94184 --- /dev/null +++ b/src/Fragments/FieldDefFragment.php @@ -0,0 +1,181 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Context; +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses the definition of a field. + * + * Used for parsing `CREATE TABLE` statement. + */ +class FieldDefFragment extends Fragment +{ + + /** + * All field options. + * + * @var array + */ + public static $FIELD_OPTIONS = array( + 'NOT NULL' => 1, + 'NULL' => 1, + 'DEFAULT' => array(2, 'var'), + 'AUTO_INCREMENT' => 3, + 'PRIMARY' => 4, + 'PRIMARY KEY' => 4, + 'UNIQUE' => 4, + 'UNIQUE KEY' => 4, + 'COMMENT' => array(5, 'var'), + 'COLUMN_FORMAT' => array(6, 'var'), + ); + + /** + * The name of the new column. + * + * @var string + */ + public $name; + + /** + * The data type of thew new column. + * + * @var DataTypeFragment + */ + public $type; + + /** + * The array of indexes. + * + * @var array + */ + public $indexes = array(); + + /** + * The options of the new field fragment. + * + * @var OptionsFragment + */ + public $options; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return FieldDefFragment[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new FieldDefFragment(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 -----------------------[ ( ]------------------------> 1 + * + * 1 -------------------[ CONSTRAINT ]-------------------> 4 + * 1 --------------------[ key type ]--------------------> 5 + * 1 -------------------[ column name ]------------------> 2 + * + * 2 -------------------[ data type ]--------------------> 3 + * + * 3 ---------------------[ size ]---------------------> 3 + * 3 ---------------------[ options ]--------------------> 4 + * + * 4 -----------------[ CONSTRAINT name ]----------------> 4 + * 4 -----------------[ CONSTRAINT type ]----------------> 5 + * + * 5 -------------------[ index names ]------------------> 6 + * + * 6 ------------------------[ , ]-----------------------> 1 + * 6 ------------------------[ ) ]-----------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($state === 0) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { + $state = 1; + } + continue; + } elseif ($state === 1) { + if ($token->type === Token::TYPE_KEYWORD) { + if ($token->value === 'CONSTRAINT') { + $state = 4; + } elseif (isset(Context::$KEY_TYPES[$token->value])) { + $expr->type = $token->value; + $state = 5; + } else { + $parser->error('Unexpected keyword.', $token); + break; // TODO: Skip to the end of the query. + } + } else { + $expr->name = $token->value; + $state = 2; + } + } elseif ($state === 2) { + $expr->type = DataTypeFragment::parse($parser, $list); + $state = 3; + } elseif ($state === 3) { + $expr->options = OptionsFragment::parse($parser, $list, static::$FIELD_OPTIONS); + $state = 6; + } elseif ($state === 4) { + if (!isset(Context::$KEY_TYPES[$token->value])) { + $expr->name = $token->value; + } else { + $expr->type = $token->value; + $state = 5; + } + } elseif ($state === 5) { + $expr->indexes = ArrayFragment::parse($parser, $list); + $state = 6; + } elseif ($state === 6) { + $ret[] = $expr; + $expr = new FieldDefFragment(); + if ($token->value === ',') { + $state = 1; + continue; + } elseif ($token->value === ')') { + ++$list->idx; + break; + } + } + + $expr->tokens[] = $token; + } + + // Last iteration was not saved. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + + } +} diff --git a/src/Fragments/FieldFragment.php b/src/Fragments/FieldFragment.php new file mode 100644 index 0000000..897a4cc --- /dev/null +++ b/src/Fragments/FieldFragment.php @@ -0,0 +1,141 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses a reference to a field. + */ +class FieldFragment extends Fragment +{ + + /** + * The name of this database. + * + * @var string + */ + public $database; + + /** + * The name of this table. + * + * @var string + */ + public $table; + + /** + * The name of the column. + * + * @var string + */ + public $column; + + /** + * The sub-expression. + * + * @var string + */ + public $expr = ''; + + /** + * The alias of this expression. + * + * @var string + */ + public $alias; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return FieldFragment + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new FieldFragment(); + + /** @var bool Whether current tokens make an expression or a table reference. */ + $isExpr = false; + + /** @var int Counts brackets. */ + $brackets = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + if ($isExpr) { + $ret->expr .= $token->token; + $ret->tokens[] = $token; + } + continue; + } + + if ($token->type === Token::TYPE_KEYWORD) { + // Keywords may be found only between brackets. + if ($brackets === 0) { + break; + } + } + + if ($token->type === Token::TYPE_OPERATOR) { + if ($token->value === '(') { + ++$brackets; + $isExpr = true; + } elseif ($token->value === ')') { + --$brackets; + if ($brackets < 0) { + $parser->error('Unexpected bracket.', $token); + $brackets = 0; + } + } elseif ($token->value === ',') { + if ($brackets === 0) { + break; + } + } + } + + if (($token->type === Token::TYPE_NUMBER) || ($token->type === Token::TYPE_BOOL) || + (($token->type === Token::TYPE_OPERATOR)) && ($token->value !== '.')) { + // Numbers, booleans and operators are usually part of expressions. + $isExpr = true; + } + + if (!$isExpr) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) { + $ret->database = $ret->table; + $ret->table = $ret->column; + } else { + if (!empty($options['skipColumn'])) { + $ret->table = $token->value; + } else { + $ret->column = $token->value; + } + } + } + + $ret->expr .= $token->token; + $ret->tokens[] = $token; + } + + if (empty($ret->tokens)) { + return null; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/FromKeyword.php b/src/Fragments/FromKeyword.php new file mode 100644 index 0000000..fd11eea --- /dev/null +++ b/src/Fragments/FromKeyword.php @@ -0,0 +1,68 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `FROM` keyword parser. + */ +class FromKeyword extends Fragment +{ + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return FieldFragment[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new FieldFragment(); + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + // No keyword is expected. + if ($token->type === Token::TYPE_KEYWORD) { + break; + } + + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) { + $ret[] = $expr; + } else { + $expr = FieldFragment::parse($parser, $list, array('skipColumn' => true)); + if ($expr === null) { + break; + } + } + + } + + // Last iteration was not saved. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/IntoKeyword.php b/src/Fragments/IntoKeyword.php new file mode 100644 index 0000000..9f6a116 --- /dev/null +++ b/src/Fragments/IntoKeyword.php @@ -0,0 +1,108 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `INTO` keyword parser. + */ +class IntoKeyword extends Fragment +{ + + /** + * The name of the table. + * + * @var string + */ + public $table; + + /** + * The name of the columns. + * + * @var array + */ + public $fields; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return IntoKeyword + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new IntoKeyword(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 ------------------------[ ( ]------------------------> 1 + * + * 1 --------------------[ field name ]-------------------> 2 + * + * 2 ------------------------[ , ]------------------------> 1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + // No keyword is expected. + if ($token->type === Token::TYPE_KEYWORD) { + break; + } + + if ($token->type === Token::TYPE_OPERATOR) { + if ($token->value === '(') { + if (empty($ret->table)) { + $parser->error('Table name was expected.', $token); + } + $state = 1; + continue; + } elseif ($token->value === ',') { + if ($state !== 2) { + $parser->error('Field name was expected.', $token); + } + $state = 1; + continue; + } + + // No other operator is expected. + break; + } + + $ret->tokens[] = $token; + if ($state === 0) { + $ret->table .= $token->value; + } elseif ($state === 1) { + $ret->fields[] = $token->value; + $state = 2; + } + + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/LimitKeyword.php b/src/Fragments/LimitKeyword.php new file mode 100644 index 0000000..08fb3a4 --- /dev/null +++ b/src/Fragments/LimitKeyword.php @@ -0,0 +1,91 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `lIMIT` keyword parser. + */ +class LimitKeyword extends Fragment +{ + + /** + * The number of rows skipped. + * + * @var int + */ + public $offset; + + /** + * The number of rows to be returned. + * + * @var int + */ + public $row_count; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return LimitKeyword + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new LimitKeyword(); + + $offset = false; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + // NOTE: `OFFSET` is not a keyword. + if (($token->type === Token::TYPE_NONE) && ($token->value === 'OFFSET')) { + if ($offset) { + $parser->error('An offset was expected.'); + } + $offset = true; + continue; + } + + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) { + $ret->offset = $ret->row_count; + $ret->row_count = 0; + continue; + } + + if ($offset) { + $ret->offset = $token->value; + $offset = false; + } else { + $ret->row_count = $token->value; + } + + $ret->tokens[] = $token; + + } + + if ($offset) { + $parser->error('An offset was expected.'); + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/OptionsFragment.php b/src/Fragments/OptionsFragment.php new file mode 100644 index 0000000..ce52135 --- /dev/null +++ b/src/Fragments/OptionsFragment.php @@ -0,0 +1,134 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses a list of options. + */ +class OptionsFragment extends Fragment +{ + + /** + * Array of selected options. + * + * @var array + */ + public $options; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return OptionsFragment + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new OptionsFragment(); + + /** @var int The ID that will be assigned to duplicate options. */ + $lastAssignedId = count($options) + 1; + + /** @var array The option that was processed last time. */ + $lastOption = null; + $lastOptionId = 0; + + $brackets = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if (isset($options[$token->value])) { + $lastOption = $options[$token->value]; + $lastOptionId = is_array($lastOption) ? $lastOption[0] : $lastOption; + + // Checking for option conflicts. + // For example, in `SELECT` statements the keywords `ALL` and `DISTINCT` + // conflict and if used together, they produce an invalid query. + // Usually, tokens can be identified in the array by the option ID, + // but if conflicts occur, a psuedo option ID is used. + // The first pseudo duplicate ID is the maximum value of the real + // options (e.g. if there are 5 options, the first fake ID is 6). + if (isset($ret->options[$lastOptionId])) { + $parser->error('This option conflicts with \'' . $ret->options[$lastOptionId] . '\'.', $token); + $lastOptionId = $lastAssignedId++; + } + } else { + // There is no option to be processed. + if ($lastOption === null) { + break; + } + + // The only keywords that are expected are those which are + // options. + if ($token->type === Token::TYPE_KEYWORD) { + break; + } + + } + + if (is_array($lastOption)) { + if (empty($ret->options[$lastOptionId])) { + $ret->options[$lastOptionId] = array('name' => $token->value, 'value' => ''); + } else { + if ($token->value !== '=') { + if ($token->value === '(') { + ++$brackets; + } elseif ($token->value === ')') { + --$brackets; + } else { + $ret->options[$lastOptionId]['value'] .= $token->value; + } + if ($brackets === 0) { + $lastOption = null; + } + } + } + } else { + $ret->options[$lastOptionId] = $token->value; + $lastOption = null; + } + $ret->tokens[] = $token; + + } + + --$list->idx; + return $ret; + } + + /** + * Checks if it has the specified option and returns it value or true. + * + * @param string $key + * + * @return mixed + */ + public function has($key) + { + foreach ($this->options as $option) { + if ((is_array($option)) && ($key === $option['name'])) { + return $option['value']; + } elseif ($key === $option) { + return true; + } + } + return false; + } +} diff --git a/src/Fragments/OrderKeyword.php b/src/Fragments/OrderKeyword.php new file mode 100644 index 0000000..5ddc8ef --- /dev/null +++ b/src/Fragments/OrderKeyword.php @@ -0,0 +1,90 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `ORDER BY` keyword parser. + */ +class OrderKeyword extends Fragment +{ + + /** + * The name of the column that is being used for ordering. + * + * @var string + */ + public $column; + + /** + * The order type. + * + * @var string + */ + public $type = 'ASC'; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return OrderKeyword[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new OrderKeyword(); + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($token->type === Token::TYPE_KEYWORD) { + // Type of ordering. By default, it is `ASC`. + if (($token->value === 'ASC') || ($token->value === 'DESC')) { + $expr->type = $token->value; + $expr->tokens[] = $token; + continue; + } + + // No other keyword is expected. + break; + } + + // Saving field. + if (($token->type === Token::TYPE_OPERATOR) && ($token->token === ',')) { + $ret[] = $expr; + $expr = new OrderKeyword(); + continue; + } + + $expr->tokens[] = $token; + $expr->column .= $token->token; + + } + + // Last iteration was not processed. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/ParamDefFragment.php b/src/Fragments/ParamDefFragment.php new file mode 100644 index 0000000..e6ecea9 --- /dev/null +++ b/src/Fragments/ParamDefFragment.php @@ -0,0 +1,125 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Context; +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * The definition of a parameter of a function or procedure. + */ +class ParamDefFragment extends Fragment +{ + + /** + * The name of the new column. + * + * @var string + */ + public $name; + + /** + * Parameter's type (IN, OUT or INOUT). + * + * @var string + */ + public $inOut; + + /** + * The data type of thew new column. + * + * @var DataTypeFragment + */ + public $type; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return ParamDefFragment[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new ParamDefFragment(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 -----------------------[ ( ]------------------------> 1 + * + * 1 ----------------[ IN / OUT / INOUT ]----------------> 1 + * 1 ----------------------[ name ]----------------------> 2 + * + * 2 -------------------[ data type ]--------------------> 3 + * + * 3 ------------------------[ , ]-----------------------> 1 + * 3 ------------------------[ ) ]-----------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($state === 0) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { + $state = 1; + } + continue; + } elseif ($state === 1) { + if (($token->value === 'IN') || ($token->value === 'OUT') || ($token->value === 'INOUT')) { + $expr->inOut = $token->value; + ++$list->idx; + } else { + $expr->name = $token->value; + $state = 2; + } + } elseif ($state === 2) { + $expr->type = DataTypeFragment::parse($parser, $list); + $state = 3; + } elseif ($state === 3) { + $ret[] = $expr; + $expr = new ParamDefFragment(); + if ($token->value === ',') { + $state = 1; + continue; + } elseif ($token->value === ')') { + ++$list->idx; + break; + } + } + + $expr->tokens[] = $token; + } + + // Last iteration was not saved. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + + } +} diff --git a/src/Fragments/RenameKeyword.php b/src/Fragments/RenameKeyword.php new file mode 100644 index 0000000..7841b2a --- /dev/null +++ b/src/Fragments/RenameKeyword.php @@ -0,0 +1,117 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `RENAME TABLE` keyword parser. + */ +class RenameKeyword extends Fragment +{ + + /** + * The old name. + * + * @var string + */ + public $old; + + /** + * The new name. + * + * @var string + */ + public $new; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return RenameKeyword + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new RenameKeyword(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 ---------------------[ old name ]--------------------> 1 + * + * 1 ------------------------[ TO ]-----------------------> 2 + * + * 2 ---------------------[ old name ]--------------------> 3 + * + * 3 ------------------------[ , ]------------------------> 0 + * 3 -----------------------[ else ]----------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($token->type === Token::TYPE_KEYWORD) { + if (($state === 1) && ($token->value === 'TO')) { + $state = 2; + continue; + } + + // No other keyword is expected. + break; + } + + if ($token->type === Token::TYPE_OPERATOR) { + if (($state === 3) && ($token->value === ',')) { + $ret[] = $expr; + $expr = new RenameKeyword(); + $state = 0; + continue; + } + + // No other operator is expected. + break; + } + + $expr->tokens[] = $token; + if ($state == 0) { + $expr->old = $token->value; + $state = 1; + } elseif ($state == 2) { + $expr->new = $token->value; + $state = 3; + } + + } + + // Last iteration was not saved. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/SelectKeyword.php b/src/Fragments/SelectKeyword.php new file mode 100644 index 0000000..f86d05d --- /dev/null +++ b/src/Fragments/SelectKeyword.php @@ -0,0 +1,83 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `SELECT` keyword parser. + */ +class SelectKeyword extends Fragment +{ + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return FieldFragment[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = null; + + /** @var bool Whether an alias is expected. */ + $alias = false; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + if ($token->type === Token::TYPE_KEYWORD) { + // No keyword is expected other than `AS`. + if ($token->value !== 'AS') { + break; + } + } + + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) { + $ret[] = $expr; + } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'AS')) { + $alias = true; + } else { + if ($alias) { + $expr->alias = $token->value; + $alias = false; + } else { + $expr = FieldFragment::parse($parser, $list); + if ($expr === null) { + break; + } + } + } + + } + + // Last iteration was not processed. + if (!empty($expr->tokens)) { + if ($alias) { + $parser->error('Alias was expected.', $token); + } + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/SetKeyword.php b/src/Fragments/SetKeyword.php new file mode 100644 index 0000000..1e70f27 --- /dev/null +++ b/src/Fragments/SetKeyword.php @@ -0,0 +1,105 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `SET` keyword parser. + */ +class SetKeyword extends Fragment +{ + + /** + * The name of the column that is being updated. + * + * @var string + */ + public $column; + + /** + * The new value. + * + * @var string + */ + public $value; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return SetKeyword[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new SetKeyword(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 -------------------[ field name ]--------------------> 1 + * + * 1 ------------------------[ , ]------------------------> 0 + * 1 ----------------------[ value ]----------------------> 1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + // No keyword is expected. + if ($token->type === Token::TYPE_KEYWORD) { + break; + } + + if ($token->type === Token::TYPE_OPERATOR) { + if ($token->value === ',') { + $ret[] = $expr; + $expr = new SetKeyword(); + $state = 0; + continue; + } elseif ($token->value === '=') { + $state = 1; + } + } + + $expr->tokens[] = $token; + if ($state === 0) { + $expr->column .= $token->value; + } else { // } else if ($state === 1) { + $expr->value = $token->value; + } + + } + + // Last iteration was not saved. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/ValuesKeyword.php b/src/Fragments/ValuesKeyword.php new file mode 100644 index 0000000..c6bd551 --- /dev/null +++ b/src/Fragments/ValuesKeyword.php @@ -0,0 +1,115 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `VALUES` keyword parser. + */ +class ValuesKeyword extends Fragment +{ + + /** + * An array with the values of the row to be inserted. + * + * @var array + */ + public $values; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return ValuesKeyword + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new ValuesKeyword(); + $value = ''; + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 ------------------------[ ( ]-----------------------> 1 + * + * 1 ----------------------[ value ]---------------------> 2 + * + * 2 ------------------------[ , ]-----------------------> 1 + * 2 ------------------------[ ) ]-----------------------> 3 + * + * 3 ---------------------[ options ]--------------------> 4 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + // No keyword is expected. + if ($token->type === Token::TYPE_KEYWORD) { + break; + } + + if ($token->type === Token::TYPE_OPERATOR) { + if ($token->value === '(') { + $state = 1; + continue; + } elseif ($token->value === ',') { + if ($state !== 3) { + $expr->values[] = $value; + $value = ''; + $state = 1; + } + continue; + } elseif ($token->value === ')') { + $state = 3; + $expr->values[] = $value; + $ret[] = $expr; + $value = ''; + $expr = new ValuesKeyword(); + continue; + } + + // No other operator is expected. + break; + } + + $expr->tokens[] = $token; + if ($state === 1) { + $value .= $token->value; + $state = 2; + } + + } + + // Last iteration was not saved. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/WhereKeyword.php b/src/Fragments/WhereKeyword.php new file mode 100644 index 0000000..786efbf --- /dev/null +++ b/src/Fragments/WhereKeyword.php @@ -0,0 +1,100 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `WHERE` keyword parser. + */ +class WhereKeyword extends Fragment +{ + + /** + * Logical operators that can be used to chain expressions. + * + * @var array + */ + private static $OPERATORS = array('&&', '(', ')', 'AND', 'OR', 'XOR', '||'); + + /** + * Whether this fragment is an operator. + * + * @var bool + */ + public $isOperator = false; + + /** + * The condition. + * + * @var string + */ + public $condition; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return WhereKeyword[] + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = array(); + + $expr = new WhereKeyword(); + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { + continue; + } + + // Conditions are delimited by logical operators. + if (in_array($token->value, static::$OPERATORS, true)) { + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + $expr = new WhereKeyword(); + $expr->isOperator = true; + $expr->condition = $token->value; + $epxr->tokens[] = $token; + $ret[] = $expr; + + $expr = new WhereKeyword(); + + continue; + } + + // No keyword is expected. + if ($token->type === Token::TYPE_KEYWORD) { + break; + } + + $expr->tokens[] = $token; + $expr->condition .= $token->token; + + } + + // Last iteration was not processed. + if (!empty($expr->tokens)) { + $ret[] = $expr; + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Lexer.php b/src/Lexer.php new file mode 100644 index 0000000..416122c --- /dev/null +++ b/src/Lexer.php @@ -0,0 +1,561 @@ +<?php + +namespace SqlParser; + +use SqlParser\Exceptions\LexerException; + +/** + * Performs lexical analysis over a SQL statement and splits it in multiple + * tokens. + * + * The output of the lexer is affected by the context of the SQL statement. + * + * @see Context + */ +class Lexer +{ + + /** + * A list of methods that are used in lexing the SQL query. + * + * @var array + */ + public static $PARSER_METHODS = array( + + // It is best to put the parsers in order of their complexity + // (ascending) and their occurance rate (descending). + // + // Conflicts: + // 1. `parseNumber` and `parseOperator` + // They fight over `+` and `-`. + // + // 2. `parseComment` and `parseOperator` + // They fight over `/` (as in ```/*comment*/``` or ```a / b```) + // + // 3. `parseBool` and `parseKeyword` + // They fight over `TRUE` and `FALSE`. + // + // 4. `parseKeyword` and `parseUnknown` + // They fight over words. `parseUnknown` does not know about keywords. + + 'parseWhitespace', 'parseNumber', 'parseComment', 'parseOperator', + 'parseBool', 'parseString', 'parseSymbol', 'parseKeyword', + 'parseUnknown' + ); + + /** + * Whether errors should throw exceptions or just be stored. + * + * @var bool + * + * @see static::$errors + */ + public $strict = false; + + /** + * The string to be parsed. + * + * @var string|UtfString + */ + public $str = ''; + + /** + * The length of `$str`. + * + * By storing its length, a lot of time is saved, because parsing methods + * would call `strlen` everytime. + * + * @var int + */ + public $len = 0; + + /** + * The index of the last parsed character. + * + * @var int + */ + public $last = 0; + + /** + * Tokens extracted from given strings. + * + * @var TokensList + */ + public $tokens; + + /** + * Statements delimiter. + * + * @var string + */ + public $delimiter = ';'; + + /** + * List of errors that occured during lexing. + * + * Usually, the lexing does not stop once an error occured because that + * error might be misdetected or a partial result (even a bad one) might be + * needed. + * + * @var LexerException[] + * + * @see Lexer::error() + */ + public $errors = array(); + + /** + * Constructor. + * + * @param string|UtfString $str + * @param bool $strict + */ + public function __construct($str, $strict = false) + { + $this->str = $str; + $this->len = ($str instanceof UtfString) ? + $str->length() : strlen($str); + $this->strict = $strict; + } + + /** + * Parses the string and extracts lexems. + * + * @param string|UtfString $str + */ + public function lex() + { + // TODO: Sometimes, static::parse* functions make unnecessary calls to + // is* functions. For a better performance, some rules can be deduced + // from context. + // For example, in `parseBool` there is no need to compare the token + // every time with `true` and `false`. The first step would be to + // compare with 'true' only and just after that add another letter from + // context and compare again with `false`. + // Another example is `parseComment`. + + $tokens = new TokensList(); + + for ($this->last = 0, $lastIdx = 0; $this->last < $this->len; $lastIdx = ++$this->last) { + /** @var Token The new token. */ + $token = null; + + foreach (static::$PARSER_METHODS as $method) { + if (($token = $this->$method())) { + break; + } + } + + if ($token === null) { + // @assert($this->last === $lastIdx); + $token = new Token($this->str[$this->last]); + if ($this->delimiter !== $this->str[$this->last]) { + $this->error('Unexpected character.', $this->str[$this->last], $this->last); + } + } + $token->position = $lastIdx; + + $tokens->tokens[$tokens->count++] = $token; + + // Handling delimiters. + if ($this->delimiter === '') { + // Updating the delimiter. + if ($token->type !== Token::TYPE_WHITESPACE) { + $this->delimiter = $token->value; + } + } elseif ($token->value === 'DELIMITER') { + // `DELIMITER` keyword found, looking for a delimiter. + $this->delimiter = ''; + } + + // Overwriting token if delimiter. + if ($token->value === $this->delimiter) { + $token->type = Token::TYPE_DELIMITER; + $token->flags = 0; + } + } + + // Adding a final delimite at the end to mark the ending. + $tokens->tokens[$tokens->count++] = new Token(null, Token::TYPE_DELIMITER); + + // Saving the tokens list. + $this->tokens = $tokens; + } + + /** + * Creates a new error log. + * + * @param string $msg + * @param string $str + * @param int $code + */ + public function error($msg = '', $str = '', $pos = 0, $code = 0) + { + $error = new LexerException($msg, $str, $pos, $code); + if ($this->strict) { + throw $error; + } + $this->errors[] = $error; + } + + /** + * Parses a keyword. + * + * @return Token + */ + public function parseKeyword() + { + $token = ''; + + /** @var Token Value to be returned. */ + $ret = null; + + /** @var int The value of `$this->last` where `$token` ends in `$this->str`. */ + $iEnd = $this->last; + + for ($j = 1; $j < Context::KEYWORD_MAX_LENGTH && $this->last < $this->len; ++$j, ++$this->last) { + $token .= $this->str[$this->last]; + if (($this->last + 1 === $this->len) || (Context::isSeparator($this->str[$this->last + 1]))) { + if (Context::isKeyword($token)) { + $ret = new Token($token, Token::TYPE_KEYWORD); + $iEnd = $this->last; + // We don't break so we find longest keyword. + // For example, `OR` and `ORDER` have a common prefix `OR`. + // If we stopped at `OR`, the parsing would be invalid. + } + } + } + + $this->last = $iEnd; + return $ret; + } + + /** + * Parses an operator. + * + * @return Token + */ + public function parseOperator() + { + $token = ''; + + /** @var Token|bool Value to be returned. */ + $ret = false; + + /** @var int The value of `$this->last` where `$token` ends in `$this->str`. */ + $iEnd = $this->last; + + for ($j = 1; $j < Context::OPERATOR_MAX_LENGTH && $this->last < $this->len; ++$j, ++$this->last) { + $token .= $this->str[$this->last]; + if ($flags = Context::isOperator($token)) { + $ret = new Token($token, Token::TYPE_OPERATOR, $flags); + $iEnd = $this->last; + } + } + + $this->last = $iEnd; + return $ret; + } + + /** + * Parses a whitespace. + * + * @return Token + */ + public function parseWhitespace() + { + $token = $this->str[$this->last]; + + if (!Context::isWhitespace($token)) { + return null; + } + + while ((++$this->last < $this->len) && (Context::isWhitespace($this->str[$this->last]))) { + $token .= $this->str[$this->last]; + } + + --$this->last; + return new Token($token, Token::TYPE_WHITESPACE); + } + + /** + * Parses a comment. + * + * @return Token + */ + public function parseComment() + { + $iBak = $this->last; + $token = $this->str[$this->last]; + + // Bash style comments. (#comment\n) + if (Context::isComment($token)) { + while ((++$this->last < $this->len) && ($this->str[$this->last] !== "\n")) { + $token .= $this->str[$this->last]; + } + $token .= $this->str[$this->last]; + return new Token($token, Token::TYPE_COMMENT, Token::FLAG_COMMENT_BASH); + } + + // C style comments. (/*comment*\/) + if (++$this->last < $this->len) { + $token .= $this->str[$this->last]; + if (Context::isComment($token)) { + $flags = Token::FLAG_COMMENT_C; + if (($this->last + 1 < $this->len) && ($this->str[$this->last + 1] === '!')) { + // It is a MySQL-specific command. + $flags |= Token::FLAG_COMMENT_MYSQL_CMD; + } + while ((++$this->last < $this->len) && + (($this->str[$this->last - 1] !== '*') || ($this->str[$this->last] !== '/'))) { + $token .= $this->str[$this->last]; + } + $token .= $this->str[$this->last]; + return new Token($token, Token::TYPE_COMMENT, $flags); + } + } + + // SQL style comments. (-- comment\n) + if (++$this->last < $this->len) { + $token .= $this->str[$this->last]; + if (Context::isComment($token)) { + if ($this->str[$this->last] !== "\n") { + // Checking if this comment did not end already (```--\n```). + while ((++$this->last < $this->len) && ($this->str[$this->last] !== "\n")) { + $token .= $this->str[$this->last]; + } + if ($this->last < $this->len) { + $token .= $this->str[$this->last]; + } + } + return new Token($token, Token::TYPE_COMMENT, Token::FLAG_COMMENT_SQL); + } + } + + $this->last = $iBak; + return null; + } + + /** + * Parses a boolean. + * + * @return Token + */ + public function parseBool() + { + if ($this->last + 3 >= $this->len) { + // At least `min(strlen('TRUE'), strlen('FALSE'))` characters are + // required. + return null; + } + + $iBak = $this->last; + $token = $this->str[$this->last] . $this->str[++$this->last] + . $this->str[++$this->last] . $this->str[++$this->last]; // _TRUE_ or _FALS_e + + if (Context::isBool($token)) { + return new Token($token, Token::TYPE_BOOL); + } elseif (++$this->last < $this->len) { + $token .= $this->str[$this->last]; // fals_E_ + if (Context::isBool($token)) { + return new Token($token, Token::TYPE_BOOL, 1); + } + } + + $this->last = $iBak; + return null; + } + + /** + * Parses a number. + * + * @return Token + */ + public function parseNumber() + { + // A rudimentary state machine is being used to parse numbers due to + // the various forms of their notation. + // + // Below are the states of the machines and the conditions to change + // the state. + // + // 1 ---------------------[ + or - ]---------------------> 1 + // 1 --------------------[ 0x or 0X ]--------------------> 2 + // 1 ---------------------[ 0 to 9 ]---------------------> 3 + // 1 ------------------------[ . ]-----------------------> 4 + // + // 2 ---------------------[ 0 to F ]---------------------> 2 + // + // 3 ---------------------[ 0 to 9 ]---------------------> 3 + // 3 ------------------------[ . ]-----------------------> 4 + // 3 ---------------------[ e or E ]---------------------> 5 + // + // 4 ---------------------[ 0 to 9 ]---------------------> 4 + // 4 ---------------------[ e or E ]---------------------> 5 + // + // 5 ----------------[ + or - or 0 to 9 ]----------------> 6 + // + // State 1 may be reached by negative numbers. + // State 2 is reached only by hex numbers. + // State 4 is reached only by float numbers. + // State 5 is reached only by numbers in approximate form. + // + // Valid final states are: 2, 3, 4 and 6. Any parsing that finished in a + // state other than these is invalid. + $iBak = $this->last; + $token = ''; + $flags = 0; + $state = 1; + for (; $this->last < $this->len; ++$this->last) { + if ($state === 1) { + if ($this->str[$this->last] === '+') { + // Do nothing. + } elseif ($this->str[$this->last] === '-') { + $flags |= Token::FLAG_NUMBER_NEGATIVE; + // Do nothing. + } elseif (($this->str[$this->last] === '0') && ($this->last + 1 < $this->len) && + (($this->str[$this->last + 1] === 'x') || ($this->str[$this->last + 1] === 'X'))) { + $token .= $this->str[$this->last++]; + $state = 2; + } elseif (($this->str[$this->last] >= '0') && ($this->str[$this->last] <= '9')) { + $state = 3; + } elseif ($this->str[$this->last] === '.') { + $state = 4; + } else { + break; + } + } elseif ($state === 2) { + $flags |= Token::FLAG_NUMBER_HEX; + if ((($this->str[$this->last] >= '0') && ($this->str[$this->last] <= '9')) || + (($this->str[$this->last] >= 'A') && ($this->str[$this->last] <= 'F')) || + (($this->str[$this->last] >= 'a') && ($this->str[$this->last] <= 'f'))) { + // Do nothing. + } else { + break; + } + } elseif ($state === 3) { + if (($this->str[$this->last] >= '0') && ($this->str[$this->last] <= '9')) { + // Do nothing. + } elseif ($this->str[$this->last] === '.') { + $state = 4; + } elseif (($this->str[$this->last] === 'e') || ($this->str[$this->last] === 'E')) { + $state = 5; + } else { + break; + } + } elseif ($state === 4) { + $flags |= Token::FLAG_NUMBER_FLOAT; + if (($this->str[$this->last] >= '0') && ($this->str[$this->last] <= '9')) { + // Do nothing. + } elseif (($this->str[$this->last] === 'e') || ($this->str[$this->last] === 'E')) { + $state = 5; + } else { + break; + } + } elseif ($state === 5) { + $flags |= Token::FLAG_NUMBER_APPROXIMATE; + if (($this->str[$this->last] === '+') || ($this->str[$this->last] === '-') || + ((($this->str[$this->last] >= '0') && ($this->str[$this->last] <= '9')))) { + $state = 6; + } else { + break; + } + } elseif ($state === 6) { + if (($this->str[$this->last] >= '0') && ($this->str[$this->last] <= '9')) { + // Do nothing. + } else { + break; + } + } + $token .= $this->str[$this->last]; + } + if (($state === 2) || ($state === 3) || (($token !== '.') && ($state === 4)) || ($state === 6)) { + --$this->last; + return new Token($token, Token::TYPE_NUMBER, $flags); + } + $this->last = $iBak; + return null; + } + + /** + * Parses a string. + * + * @return Token + */ + public function parseString() + { + $quote = $token = $this->str[$this->last]; + if (!($flags = Context::isString($token))) { + return null; + } + while ((++$this->last < $this->len) && ($this->str[$this->last] !== $quote)) { + $token .= $this->str[$this->last]; + if (($this->str[$this->last] === '\\') && (++$this->last < $this->len)) { + $token .= $this->str[$this->last]; + } + } + if (($this->last >= $this->len) || ($this->str[$this->last] !== $quote)) { + $this->error('Ending quote ' . $quote . ' was expected.', '', $this->last); + } else { + $token .= $this->str[$this->last]; + } + return new Token($token, Token::TYPE_STRING, $flags); + } + + /** + * Parses a symbol. + * + * @return Token + */ + public function parseSymbol() + { + // TODO: `@` can be used in queries like ```... FROM 'user'@'%' ...```. + $token = $this->str[$this->last]; + if (!($flags = Context::isSymbol($token))) { + return null; + } + if ($flags === Token::FLAG_SYMBOL_VARIABLE) { + ++$this->last; + if (($name = static::parseString($this->str, $this->last, $this->len))) { + $token .= $name->token; + } elseif (($name = static::parseSymbol($this->str, $this->last, $this->len))) { + $token .= $name->token; + } else { + $name = static::parseUnknown($this->str, $this->last, $this->len); + if ($name === null) { + $this->error('Variable name was expected.', $this->str[$this->last], $this->last); + return null; + } + $token .= $name->token; + } + } elseif ($flags === Token::FLAG_SYMBOL_BACKTICK) { + $token = $this->str[$this->last]; + while ((++$this->last < $this->len) && ($this->str[$this->last] !== '`')) { + $token .= $this->str[$this->last]; + } + if ($this->last >= $this->len) { + $this->error('Ending backtick ` was expected.', '', $this->last); + } else { + $token .= $this->str[$this->last]; + } + } + return new Token($token, Token::TYPE_SYMBOL, $flags); + } + + /** + * Parses unknown parts of the query. + * + * @return Token + */ + public function parseUnknown() + { + $token = $this->str[$this->last]; + if (Context::isSeparator($token)) { + return null; + } + while ((++$this->last < $this->len) && (!Context::isSeparator($this->str[$this->last]))) { + $token .= $this->str[$this->last]; + } + --$this->last; + return new Token($token); + } +} diff --git a/src/Parser.php b/src/Parser.php new file mode 100644 index 0000000..9fab5ac --- /dev/null +++ b/src/Parser.php @@ -0,0 +1,183 @@ +<?php + +namespace SqlParser; + +use SqlParser\Exceptions\ParserException; + +/** + * Takes multiple tokens (contained in a Lexer instance) as input and builds a + * parse tree. + */ +class Parser +{ + + /** + * Array of classes that are used in parsing the SQL statements. + * + * @var array + */ + public static $STATEMENT_PARSERS = array( + + // Data Definition Statements. + // https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-definition.html + 'ALTER' => '', + 'CREATE' => 'SqlParser\\Statements\\CreateStatement', + 'DROP' => '', + 'RENAME' => 'SqlParser\\Statements\\RenameStatement', + 'TRUNCATE' => '', + + // Data Manipulation Statements. + // https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-manipulation.html + 'CALL' => 'SqlParser\\Statements\\CallStatement', + 'DELETE' => 'SqlParser\\Statements\\DeleteStatement', + 'DO' => '', + 'HANDLER' => '', + 'INSERT' => 'SqlParser\\Statements\\InsertStatement', + 'LOAD' => '', + 'REPLACE' => 'SqlParser\\Statements\\ReplaceStatement', + 'SELECT' => 'SqlParser\\Statements\\SelectStatement', + 'UPDATE' => 'SqlParser\\Statements\\UpdateStatement', + + // Prepared Statements. + // https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html + 'PREPARE' => '', + 'EXECUTE' => '', + ); + + /** + * Array of classes that are used in parsing SQL fragments. + * + * @var array + */ + public static $KEYWORD_PARSERS = array( + + // Meta-keywords. + '_ARRAY' => 'SqlParser\\Fragments\\ArrayFragment', + '_CALL' => 'SqlParser\\Fragments\\CallKeyword', + '_CREATE_DEF' => 'SqlParser\\Fragments\\CreateDefFragment', + '_DATA_TYPE' => 'SqlParser\\Fragments\\CreateDefFragment', + '_FIELD' => 'SqlParser\\Fragments\\FieldFragment', + '_FIELD_DEF' => 'SqlParser\\Fragments\\FieldDefFragment', + '_OPTIONS' => 'SqlParser\\Fragments\\OptionsFragment', + '_RENAME' => 'SqlParser\\Fragments\\RenameKeyword', + '_SELECT' => 'SqlParser\\Fragments\\SelectKeyword', + + 'FROM' => 'SqlParser\\Fragments\\FromKeyword', + 'GROUP' => 'SqlParser\\Fragments\\GroupFragment', + 'HAVING' => 'SqlParser\\Fragments\\WhereKeyword', + 'INTO' => 'SqlParser\\Fragments\\IntoKeyword', + 'LIMIT' => 'SqlParser\\Fragments\\LimitKeyword', + 'ORDER' => 'SqlParser\\Fragments\\OrderKeyword', + 'PARTITION' => 'SqlParser\\Fragments\\ArrayFragment', + 'SET' => 'SqlParser\\Fragments\\SetKeyword', + 'VALUE' => 'SqlParser\\Fragments\\ValuesKeyword', + 'VALUES' => 'SqlParser\\Fragments\\ValuesKeyword', + 'WHERE' => 'SqlParser\\Fragments\\WhereKeyword', + + ); + + /** + * The list of tokens that are parsed. + * + * @var TokensList + */ + public $list; + + /** + * Whether errors should throw exceptions or just be stored. + * + * @var bool + * + * @see static::$errors + */ + public $strict = false; + + /** + * List of errors that occured during parsing. + * + * Usually, the parsing does not stop once an error occured because that + * error might be misdetected or a partial result (even a bad one) might be + * needed. + * + * @var ParserException[] + * + * @see Parser::error() + */ + public $errors = array(); + + /** + * List of statements parsed. + * + * @var Statement[] + */ + public $statements = array(); + + /** + * Constructor. + * + * @param Parser $parser + * @param TokensList $list + * @param bool $strict + */ + public function __construct(TokensList $list, $strict = false) + { + $this->list = $list; + $this->strict = $strict; + } + + /** + * Builds the parse trees. + */ + public function parse() + { + $tokens = &$this->list->tokens; + $count = &$this->list->count; + $last = &$this->list->idx; + + for (; $last < $count; ++$last) { + /** @var Token Token parsed at this moment. */ + $token = $tokens[$last]; + + // Statements can start with keywords only. + // Comments, whitespaces, etc. are ignored. + if ($token->type !== Token::TYPE_KEYWORD) { + continue; + } + + // Checking if it is a known statement that can be parsed. + if (empty(static::$STATEMENT_PARSERS[$token->value])) { + $this->error( + 'Unrecognized statement type "' . $token->value .'".', + $token + ); + // TODO: Skip to first delimiter. + continue; + } + + /** @var string The name of the class that is used for parsing. */ + $class = static::$STATEMENT_PARSERS[$token->value]; + + /** @var Statement Processed statement. */ + $stmt = new $class(); + + $stmt->parse($this, $this->list); + $this->statements[] = $stmt; + } + } + + /** + * Creates a new error log. + * + * @param string $str + * @param Token $token + * @param int $code + */ + public function error($str = '', Token $token = null, $code = 0) + { + $error = new ParserException($str, $token, $code); + if ($this->strict) { + throw $error; + } + $this->errors[] = $error; + } +} diff --git a/src/Statement.php b/src/Statement.php new file mode 100644 index 0000000..c429fce --- /dev/null +++ b/src/Statement.php @@ -0,0 +1,153 @@ +<?php + +namespace SqlParser; + +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Statement; +use SqlParser\Token; + +use SqlParser\Fragments\ArrayFragment; +use SqlParser\Fragments\CallKeyword; +use SqlParser\Fragments\CreateDefFragment; +use SqlParser\Fragments\DataTypeFragment; +use SqlParser\Fragments\FieldDefFragment; +use SqlParser\Fragments\FieldFragment; +use SqlParser\Fragments\FromKeyword; +use SqlParser\Fragments\IntoKeyword; +use SqlParser\Fragments\LimitKeyword; +use SqlParser\Fragments\OptionsFragment; +use SqlParser\Fragments\OrderKeyword; +use SqlParser\Fragments\ParamDefFragment; +use SqlParser\Fragments\RenameKeyword; +use SqlParser\Fragments\SelectKeyword; +use SqlParser\Fragments\SetKeyword; +use SqlParser\Fragments\ValuesKeyword; +use SqlParser\Fragments\WhereKeyword; + +abstract class Statement +{ + + /** + * The index of the first token used in this statement. + * + * @var int + */ + public $first; + + /** + * The index of the last token used in this statement. + * + * @var int + */ + public $last; + + /** + * Parses the statements defined by the tokens list. + * + * @param Parser $parser + * @param TokensList $list + */ + public function parse(Parser $parser, TokensList $list) + { + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $token = $list->tokens[$list->idx]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Only keywords are relevant here. Other parts of the query are + // processed in the functions below. + if ($token->type !== Token::TYPE_KEYWORD) { + continue; + } + + /** @var string The name of the class that is used for parsing. */ + $class = null; + + if (!empty(Parser::$KEYWORD_PARSERS[$token->value])) { + $class = Parser::$KEYWORD_PARSERS[$token->value]; + } elseif (!empty(Parser::$STATEMENT_PARSERS[$token->value])) { + // The keyword we are processing right now is the beginning of a + // statement and they are usually handled differently. + } else { + $parser->error( + 'Unrecognized keyword "' . $token->value . '".', + $token + ); + continue; + } + + /** @var string The name of the field where the result is stored. */ + $field = strtolower($token->value); + + // Parsing options. + if (($class == null) && (isset(static::$OPTIONS))) { + ++$list->idx; // Skipping keyword. + $this->options = OptionsFragment::parse($parser, $list, static::$OPTIONS); + } + + // Keyword specific code. + if ($token->value === 'CALL') { + ++$list->idx; + $this->call = CallKeyword::parse($parser, $list); + } elseif ($token->value === 'CREATE') { + ++$list->idx; + $this->name = CreateDefFragment::parse($parser, $list); + if ($this->options->has('TABLE')) { + ++$list->idx; + $this->fields = FieldDefFragment::parse($parser, $list); + ++$list->idx; + $this->tableOptions = OptionsFragment::parse($parser, $list, CreateDefFragment::$TABLE_OPTIONS); + } elseif (($this->options->has('PROCEDURE')) || ($this->options->has('FUNCTION'))) { + ++$list->idx; + $this->parameters = ParamDefFragment::parse($parser, $list); + if ($this->options->has('FUNCTION')) { + $token = $list->getNextOfType(Token::TYPE_KEYWORD); + if ($token->value !== 'RETURNS') { + $parser->error('\'RETURNS\' keyword was expected.', $token); + } else { + ++$list->idx; + $this->return = DataTypeFragment::parse($parser, $list); + } + } + ++$list->idx; + $this->funcOptions = OptionsFragment::parse($parser, $list, CreateDefFragment::$FUNC_OPTIONS); + ++$list->idx; + $this->body = array(); + for (; $list->idx < $list->count; ++$list->idx) { + $token = $list->tokens[$list->idx]; + $this->body[] = $token; + if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'END')) { + break; + } + } + $class = null; // The statement has been processed here. + } + } elseif (($token->value === 'GROUP') || ($token->value === 'ORDER')) { + $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'BY'); + } elseif ($token->value === 'RENAME') { + $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'TABLE'); + ++$list->idx; + $this->renames = RenameKeyword::parse($parser, $list); + } elseif ($token->value === 'SELECT') { + ++$list->idx; // Skipping last option. + $this->expr = SelectKeyword::parse($parser, $list); + } elseif ($token->value === 'UPDATE') { + ++$list->idx; // Skipping last option. + $this->from = FromKeyword::parse($parser, $list); + } + + // Finally, processing the keyword (if possible). + if ($class !== null) { + ++$list->idx; // Skipping keyword. + $this->$field = $class::parse($parser, $list, array()); + } + } + + $this->last = --$list->idx; // Go back to last used token. + } +} diff --git a/src/Statements/CallStatement.php b/src/Statements/CallStatement.php new file mode 100644 index 0000000..76681b5 --- /dev/null +++ b/src/Statements/CallStatement.php @@ -0,0 +1,19 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `CALL` statement. + */ +class CallStatement extends Statement +{ + + /** + * The name of the function and its parameters. + * + * @var CallKeyword + */ + public $call; +} diff --git a/src/Statements/CreateStatement.php b/src/Statements/CreateStatement.php new file mode 100644 index 0000000..edf0dbd --- /dev/null +++ b/src/Statements/CreateStatement.php @@ -0,0 +1,73 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `CREATE` statement. + */ +class CreateStatement extends Statement +{ + + /** + * Options for `CREATE` statements. + * + * @var array + */ + public static $OPTIONS = array( + + 'DATABASE' => 1, + 'EVENT' => 1, + 'FUNCTION' => 1, + 'INDEX' => 1, + 'PROCEDURE' => 1, + 'SERVER' => 1, + 'TABLE' => 1, + 'TABLESPACE' => 1, + 'TRIGGER' => 1, + 'VIEW' => 1, + + 'TEMPORARY' => 2, + 'IF NOT EXISTS' => 3, + ); + + /** + * The name of the enw table. + * + * @var CreateDefFragment + */ + public $name; + + /** + * The options of this query. + * + * @var OptionsFragment + * + * @see static::$OPTIONS + */ + public $options; + + /** + * The options of the table. + * + * @var OptionsFragment + * + * @see CreateDefFragment::$TABLE_OPTIONS + */ + public $tableOptions; + + /** + * Field created by this statement. + * + * @var FieldDefFragment[] + */ + public $fields; + + /** + * The body of this function or procedure. + * + * @var Token[] + */ + public $body; +} diff --git a/src/Statements/DeleteStatement.php b/src/Statements/DeleteStatement.php new file mode 100644 index 0000000..01dd8ad --- /dev/null +++ b/src/Statements/DeleteStatement.php @@ -0,0 +1,74 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `DELETE` statement. + * + * DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name + * [PARTITION (partition_name,...)] + * [WHERE where_condition] + * [ORDER BY ...] + * [LIMIT row_count] + * + */ +class DeleteStatement extends Statement +{ + + /** + * Options for `DELETE` statements. + * + * @var array + */ + public static $OPTIONS = array( + 'LOW_PRIORITY' => 1, + 'QUICK' => 2, + 'IGNORE' => 3, + ); + + /** + * The options of this query. + * + * @var OptionsFragment + * + * @see static::$OPTIONS + */ + public $options; + + /** + * Tables used as sources for this statement. + * + * @var FieldFragment[] + */ + public $from; + + /** + * Partitions used as source for this statement. + * + * @var ArrayFragment + */ + public $partition; + + /** + * Conditions used for filtering each row of the result set. + * + * @var WhereKeyword[] + */ + public $where; + + /** + * Specifies the order of the rows in the result set. + * + * @var OrderKeyword[] + */ + public $order; + + /** + * Conditions used for limiting the size of the result set. + * + * @var LimitKeyword + */ + public $limit; +} diff --git a/src/Statements/InsertStatement.php b/src/Statements/InsertStatement.php new file mode 100644 index 0000000..f24634a --- /dev/null +++ b/src/Statements/InsertStatement.php @@ -0,0 +1,78 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `INSERT` statement. + * + * INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] + * [INTO] tbl_name + * [PARTITION (partition_name,...)] + * [(col_name,...)] + * {VALUES | VALUE} ({expr | DEFAULT},...),(...),... + * [ ON DUPLICATE KEY UPDATE + * col_name=expr + * [, col_name=expr] ... ] + * + * or + * + * INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] + * [INTO] tbl_name + * [PARTITION (partition_name,...)] + * SET col_name={expr | DEFAULT}, ... + * [ ON DUPLICATE KEY UPDATE + * col_name=expr + * [, col_name=expr] ... ] + * + * or + * + * INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE] + * [INTO] tbl_name + * [PARTITION (partition_name,...)] + * [(col_name,...)] + * SELECT ... + * [ ON DUPLICATE KEY UPDATE + * col_name=expr + * [, col_name=expr] ... ] + * + */ +class InsertStatement extends Statement +{ + + /** + * Options for `INSERT` statements. + * + * @var array + */ + public static $OPTIONS = array( + 'LOW_PRIORITY' => 1, + 'DELAYED' => 2, + 'HIGH_PRIORITY' => 3, + 'IGNORE' => 4, + ); + + /** + * The options of this query. + * + * @var OptionsFragment + * + * @see static::$OPTIONS + */ + public $options; + + /** + * Tables used as target for this statement. + * + * @var IntoKeyword + */ + public $into; + + /** + * Values to be inserted. + * + * @var ValuesKeyword + */ + public $values; +} diff --git a/src/Statements/RenameStatement.php b/src/Statements/RenameStatement.php new file mode 100644 index 0000000..bd3c767 --- /dev/null +++ b/src/Statements/RenameStatement.php @@ -0,0 +1,23 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `RENAME` statement. + * + * RENAME TABLE tbl_name TO new_tbl_name + * [, tbl_name2 TO new_tbl_name2] ... + * + */ +class RenameStatement extends Statement +{ + + /** + * The old and new names of the tables. + * + * @var RenameKeyword[] + */ + public $renames; +} diff --git a/src/Statements/ReplaceStatement.php b/src/Statements/ReplaceStatement.php new file mode 100644 index 0000000..8e69207 --- /dev/null +++ b/src/Statements/ReplaceStatement.php @@ -0,0 +1,63 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `REPLACE` statement. + * + * REPLACE [LOW_PRIORITY | DELAYED] + * [INTO] tbl_name [(col_name,...)] + * {VALUES | VALUE} ({expr | DEFAULT},...),(...),... + * + * or + * + * REPLACE [LOW_PRIORITY | DELAYED] + * [INTO] tbl_name + * SET col_name={expr | DEFAULT}, ... + * + */ +class ReplaceStatement extends Statement +{ + + /** + * Options for `REPLACE` statements and their slot ID. + * + * @var array + */ + public static $OPTIONS = array( + 'LOW_PRIORITY' => 1, + 'DELAYED' => 1, + ); + + /** + * The options of this query. + * + * @var OptionsFragment + * + * @see static::$OPTIONS + */ + public $options; + + /** + * Tables used as target for this statement. + * + * @var IntoKeyword + */ + public $into; + + /** + * Values to be replaced. + * + * @var ValuesKeyword + */ + public $values; + + /** + * The replaced values. + * + * @var SetKeyword[] + */ + public $set; +} diff --git a/src/Statements/SelectStatement.php b/src/Statements/SelectStatement.php new file mode 100644 index 0000000..b8f3e10 --- /dev/null +++ b/src/Statements/SelectStatement.php @@ -0,0 +1,123 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `SELECT` statement. + * + * SELECT + * [ALL | DISTINCT | DISTINCTROW ] + * [HIGH_PRIORITY] + * [MAX_STATEMENT_TIME = N] + * [STRAIGHT_JOIN] + * [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] + * [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] + * select_expr [, select_expr ...] + * [FROM table_references + * [PARTITION partition_list] + * [WHERE where_condition] + * [GROUP BY {col_name | expr | position} + * [ASC | DESC], ... [WITH ROLLUP]] + * [HAVING where_condition] + * [ORDER BY {col_name | expr | position} + * [ASC | DESC], ...] + * [LIMIT {[offset,] row_count | row_count OFFSET offset}] + * [PROCEDURE procedure_name(argument_list)] + * [INTO OUTFILE 'file_name' + * [CHARACTER SET charset_name] + * export_options + * | INTO DUMPFILE 'file_name' + * | INTO var_name [, var_name]] + * [FOR UPDATE | LOCK IN SHARE MODE]] + * + */ +class SelectStatement extends Statement +{ + + /** + * Options for `SELECT` statements and their slot ID. + * + * @var array + */ + public static $OPTIONS = array( + 'ALL' => 1, + 'DISTINCT' => 1, + 'DISTINCTROW' => 1, + 'HIGH_PRIORITY' => 2, + 'MAX_STATEMENT_TIME' => array(3, 'var'), + 'STRAIGHT_JOIN' => 4, + 'SQL_SMALL_RESULT' => 5, + 'SQL_BIG_RESULT' => 6, + 'SQL_BUFFER_RESULT' => 7, + 'SQL_CACHE' => 8, + 'SQL_NO_CACHE' => 8, + 'SQL_CALC_FOUND_ROWS' => 9, + ); + + /** + * The options of this query. + * + * @var OptionsFragment + * + * @see static::$OPTIONS + */ + public $options; + + /** + * Expressions that are being selected by this statement. + * + * @var FieldFragment[] + */ + public $expr; + + /** + * Tables used as sources for this statement. + * + * @var FieldFragment[] + */ + public $from; + + /** + * Partitions used as source for this statement. + * + * @var ArrayFragment[] + */ + public $partition; + + /** + * Conditions used for filtering each row of the result set. + * + * @var WhereKeyword + */ + public $where; + + /** + * Conditions used for grouping the result set. + * + * @var GroupKeyword + */ + public $group; + + /** + * Conditions used for filtering the result set. + * + * @var WhereKeyword[] + */ + public $having; + + /** + * Specifies the order of the rows in the result set. + * + * @var OrderKeyword[] + */ + public $order; + + /** + * Conditions used for limiting the size of the result set. + * + * @var LimitKeyword + */ + public $limit; +} diff --git a/src/Statements/UpdateStatement.php b/src/Statements/UpdateStatement.php new file mode 100644 index 0000000..cdeab03 --- /dev/null +++ b/src/Statements/UpdateStatement.php @@ -0,0 +1,79 @@ +<?php + +namespace SqlParser\Statements; + +use SqlParser\Statement; + +/** + * `UPDATE` statement. + * + * UPDATE [LOW_PRIORITY] [IGNORE] table_reference + * SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ... + * [WHERE where_condition] + * [ORDER BY ...] + * [LIMIT row_count] + * + * or + * + * UPDATE [LOW_PRIORITY] [IGNORE] table_references + * SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ... + * [WHERE where_condition] + * + */ +class UpdateStatement extends Statement +{ + + /** + * Options for `UPDATE` statements and their slot ID. + * + * @var array + */ + public static $OPTIONS = array( + 'LOW_PRIORITY' => 1, + 'IGNORE' => 2, + ); + + /** + * The options of this query. + * + * @var OptionsFragment + * + * @see static::$OPTIONS + */ + public $options; + + /** + * Tables used as sources for this statement. + * + * @var FieldFragment[] + */ + public $from; + + /** + * The updated values. + * + * @var SetKeyword[] + */ + public $set; + + /** + * Conditions used for filtering each row of the result set. + * + * @var WhereKeyword[] + */ + public $where; + + /** + * Specifies the order of the rows in the result set. + * + * @var OrderKeyword[] + */ + public $order; + + /** + * Conditions used for limiting the size of the result set. + * + * @var LimitKeyword + */ + public $limit; +} diff --git a/src/Token.php b/src/Token.php new file mode 100644 index 0000000..e597713 --- /dev/null +++ b/src/Token.php @@ -0,0 +1,253 @@ +<?php + +namespace SqlParser; + +/** + * A structure representing a lexeme that explicitly indicates its + * categorization for the purpose of parsing. + */ +class Token +{ + + // Types of tokens (a vague description of a token's purpose). + + /** + * This type is used when the token is invalid or its type cannot be + * determiend because of the ambigous context. Further analysis might be + * required to detect its type. + * + * @var int + */ + const TYPE_NONE = 0; + + /** + * SQL specific keywords: SELECT, UPDATE, INSERT, etc. + * + * @var int + */ + const TYPE_KEYWORD = 1; + + /** + * Any type of legal operator. + * + * Arithmetic operators: +, -, *, /, etc. + * Logical operators: ===, <>, !==, etc. + * Bitwise operators: &, |, ^, etc. + * Assignment operators: =, +=, -=, etc. + * SQL specific operators: . (e.g. .. WHERE database.table ..), + * * (e.g. SELECT * FROM ..) + * + * @var int + */ + const TYPE_OPERATOR = 2; + + /** + * Spaces, tabs, new lines, etc. + * + * @var int + */ + const TYPE_WHITESPACE = 3; + + /** + * Any type of legal comment. + * + * Bash (#), C (/* *\/) or SQL (--) comments: + * + * -- SQL-comment + * + * #Bash-like comment + * + * /*C-like comment*\/ + * + * or: + * + * /*C-like + * comment*\/ + * + * Backslashes were added to respect PHP's comments syntax. + * + * @var int + */ + const TYPE_COMMENT = 4; + + /** + * Boolean values: true or false. + * + * @var int + */ + const TYPE_BOOL = 5; + + /** + * Numbers: 4, 0x8, 15.16, 23e42, etc. + * + * @var int + */ + const TYPE_NUMBER = 6; + + /** + * Literal strings: 'string', "test". + * Some of these strings are actually symbols. + * + * @var int + */ + const TYPE_STRING = 7; + + /** + * Database, table names, variables, etc. + * For example: ```SELECT `foo`, `bar` FROM `database`.`table`;``` + * + * @var int + */ + const TYPE_SYMBOL = 8; + + /** + * Delimits an unknown string. + * For example: ```SELECT * FROM test;```, `test` is a delimiter. + * + * @var int + */ + const TYPE_DELIMITER = 9; + + // Flags that describe the tokens in more detail. + + // Numbers related flags. + const FLAG_NUMBER_HEX = 1; + const FLAG_NUMBER_FLOAT = 2; + const FLAG_NUMBER_APPROXIMATE = 4; + const FLAG_NUMBER_NEGATIVE = 8; + + // Strings related flags. + const FLAG_STRING_SINGLE_QUOTES = 1; + const FLAG_STRING_DOUBLE_QUOTES = 2; + + // Comments related flags. + const FLAG_COMMENT_BASH = 1; + const FLAG_COMMENT_C = 2; + const FLAG_COMMENT_SQL = 4; + const FLAG_COMMENT_MYSQL_CMD = 8; + + // Operators related flags. + const FLAG_OPERATOR_ARITHMETIC = 1; + const FLAG_OPERATOR_LOGICAL = 2; + const FLAG_OPERATOR_BITWISE = 4; + const FLAG_OPERATOR_ASSIGNMENT = 8; + const FLAG_OPERATOR_SQL = 16; + + // Symbols related flags. + const FLAG_SYMBOL_VARIABLE = 1; + const FLAG_SYMBOL_BACKTICK = 2; + + /** + * The token it its raw string representation. + * + * @var string + */ + public $token; + + /** + * The value this token contains (i.e. token after some evaluation) + * + * @var mixed + */ + public $value; + + /** + * The type of this token. + * + * @var int + */ + public $type; + + /** + * The flags of this token. + * + * @var int + */ + public $flags; + + /** + * The position in the inial string where this token started. + * + * @var int + */ + public $position; + + /** + * Constructor. + * + * @param string $token + * @param int $type + * @param int $flags + * @param int $position + */ + public function __construct($token, $type = 0, $flags = 0) + { + $this->token = $token; + $this->type = $type; + $this->flags = $flags; + $this->value = $this->extract(); + } + + /** + * Does little processing to the token to extract a value. + * + * If no processing can be done it will return the initial string. + * + * @return mixed + */ + public function extract() + { + switch ($this->type) { + case Token::TYPE_KEYWORD: + return strtoupper($this->token); + case Token::TYPE_WHITESPACE: + return ' '; + case Token::TYPE_BOOL: + return strtoupper($this->token) === 'TRUE'; + case Token::TYPE_NUMBER: + $ret = str_replace('--', '', $this->token); // e.g. ---42 === -42 + if ($this->flags & Token::FLAG_NUMBER_HEX) { + if ($this->flags & Token::FLAG_NUMBER_NEGATIVE) { + $ret = str_replace('-', '', $this->token); + sscanf($ret, "%x", $ret); + $ret = -$ret; + } else { + sscanf($ret, "%x", $ret); + } + } elseif (($this->flags & Token::FLAG_NUMBER_APPROXIMATE) || + ($this->flags & Token::FLAG_NUMBER_FLOAT)) { + sscanf($ret, "%f", $ret); + } else { + sscanf($ret, "%d", $ret); + } + return $ret; + case Token::TYPE_STRING: + return mb_substr($this->token, 1, -1); // trims quotes + case Token::TYPE_SYMBOL: + if ($this->flags & Token::FLAG_SYMBOL_VARIABLE) { + if ($this->token[1] === '`') { + return mb_substr($this->token, 2, -1); // trims @` and ` + } else { + return mb_substr($this->token, 1); // trims @ + } + } elseif ($this->flags & Token::FLAG_SYMBOL_BACKTICK) { + return mb_substr($this->token, 1, -1); // trims backticks + } + } + return $this->token; + } + + /** + * Converts the token into an inline token by replacing tabs and new lines. + * + * @return string + */ + public function getInlineToken() + { + return str_replace( + array("\r", "\n", "\t"), + array('\r', '\n', '\t'), + $this->token + ); + } +} diff --git a/src/TokensList.php b/src/TokensList.php new file mode 100644 index 0000000..405f4a1 --- /dev/null +++ b/src/TokensList.php @@ -0,0 +1,118 @@ +<?php + +namespace SqlParser; + +class TokensList implements \ArrayAccess +{ + + /** + * The array of tokens. + * + * @var array + */ + public $tokens = array(); + + /** + * The count of tokens. + * + * @var int + */ + public $count = 0; + + /** + * The index of the next token to be returned. + * + * @var int + */ + public $idx = 0; + + /** + * Adds a new token. + * + * @param Token $token + */ + public function add(Token $token) + { + $this->tokens[$this->count++] = $token; + } + + /** + * Gets next token in list. + * + * @return Token + */ + public function getNext() + { + if ($this->idx < $this->count) { + return $this->tokens[$this->idx++]; + } + return null; + } + + /** + * Gets next token of a specified type. + * + * @param int $type + * + * @return Token + */ + public function getNextOfType($type) + { + for (; $this->idx < $this->count; ++$this->idx) { + if ($this->tokens[$this->idx]->type === $type) { + return $this->tokens[$this->idx++]; + } + } + return null; + } + + /** + * Gets next token of a specified type. + * + * @param int $type + * @param string $value + * + * @return Token + */ + public function getNextOfTypeAndValue($type, $value) + { + for (; $this->idx < $this->count; ++$this->idx) { + if (($this->tokens[$this->idx]->type === $type) && + ($this->tokens[$this->idx]->value === $value)) { + return $this->tokens[$this->idx++]; + } + } + return null; + } + + // ArrayAccess methods. + + public function offsetSet($offset, $value) + { + if ($offset === null) { + $this->tokens[$this->count++] = $value; + } else { + $this->tokens[$offset] = $value; + } + } + + public function offsetGet($offset) + { + return $offset < $this->count ? $this->tokens[$offset] : null; + } + + public function offsetExists($offset) + { + return $offset < $this->count; + } + + public function offsetUnset($offset) + { + unset($this->tokens[$offset]); + --$this->count; + for ($i = $offset; $i < $this->count; ++$i) { + $this->tokens[$i] = $this->tokens[$i + 1]; + } + unset($this->tokens[$this->count]); + } +} diff --git a/src/UtfString.php b/src/UtfString.php new file mode 100644 index 0000000..8815e58 --- /dev/null +++ b/src/UtfString.php @@ -0,0 +1,220 @@ +<?php + +namespace SqlParser; + +/** + * Implements array-like access for UTF-8 strings. + * + * In this library, this class should be used to parse UTF-8 queries. + */ +class UtfString implements \ArrayAccess +{ + + /** + * The raw, multibyte string. + * + * @var string + */ + private $str = ''; + + /** + * The index of current byte. + * + * For ASCII strings, the byte index is equal to the character index. + * + * @var int + */ + private $byteIdx = 0; + + /** + * The index of current character. + * + * For non-ASCII strings, some characters occupy more than one byte and + * the character index will have a lower value than the byte index. + * + * @var int + */ + private $charIdx = 0; + + /** + * The length of the string (in bytes). + * + * @var int + */ + private $buffLen = 0; + + /** + * The length of the string (in characters). + * + * @var int + */ + private $charLen = 0; + + /** + * Constructor. + * + * @param string $str + */ + public function __construct($str) + { + $this->str = $str; + $this->byteIdx = 0; + $this->charIdx = 0; + // TODO: `strlen($str)` might return a wrong length when function + // overloading is enabled. + // https://php.net/manual/ro/mbstring.overload.php + $this->byteLen = strlen($str); + $this->charLen = mb_strlen($str); + } + + /** + * Checks if the given offset exists. + * + * @param int $offset + * + * @return bool + */ + public function offsetExists($offset) + { + return $offset < $charLen; + } + + /** + * Gets the character at given offset. + * + * @param int $offset + * + * @return string + */ + public function offsetGet($offset) + { + if (($offset < 0) || ($offset >= $this->charLen)) { + return null; + } + + $delta = $offset - $this->charIdx; + + if ($delta > 0) { + // Fast forwarding. + while ($delta-- > 0) { + $this->byteIdx += static::getCharLength($this->str[$this->byteIdx]); + ++$this->charIdx; + } + } elseif ($delta < 0) { + // Rewinding. + while ($delta++ < 0) { + do { + $byte = ord($this->str[--$this->byteIdx]); + } while ((128 <= $byte) && ($byte < 192)); + --$this->charIdx; + } + } + + $bytesCount = static::getCharLength($this->str[$this->byteIdx]); + + $ret = ''; + for ($i = 0; $bytesCount-- > 0; ++$i) { + $ret .= $this->str[$this->byteIdx + $i]; + } + + return $ret; + } + + /** + * Sets the value of a character. + * + * @param int $offset + * @param string $value + */ + public function offsetSet($offset, $value) + { + throw new \Exception('Not implemented.'); + } + + /** + * Unsets an index. + * + * @param int $offset + */ + public function offsetUnset($offset) + { + throw new \Exception('Not implemented.'); + } + + /** + * Gets the length of an UTF-8 character. + * + * According to RFC 3629, a UTF-8 character can have at most 4 bytes. + * However, this implemenation supports UTF-8 characters containing up to 6 + * bytes. + * + * @see http://tools.ietf.org/html/rfc3629 + * + * @param string $byte + * + * @return int + */ + private static function getCharLength($byte) + { + $byte = ord($byte); + if ($byte < 128) { + return 1; + } elseif ($byte < 224) { + return 2; + } elseif ($byte < 240) { + return 3; + } elseif ($byte < 248) { + return 4; + } elseif ($byte === 252) { + return 5; // unofficial + } + return 6; // unofficial + } + + /** + * Returns the number of remaining characters. + * + * @return int + */ + public function remaining() + { + if ($this->charIdx < $this->charLen) { + return $this->charLen - $this->charIdx; + } + return 0; + } + + /** + * Returns the length in characters of the string. + * + * @return int + */ + public function length() + { + return $this->charLen; + } + + /** + * Gets the values of the indexes. + * + * @param int &$byte + * @param int &$char + */ + public function getIndexes(&$byte, &$char) + { + $byte = $this->byteIdx; + $char = $this->charIdx; + } + + /** + * Sets the values of the indexes. + * + * @param int $byte + * @param int $char + */ + public function setIndexes($byte = 0, $char = 0) + { + $this->byteIdx = $byte; + $this->charIdx = $char; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..03a7aa4 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,66 @@ +<?php + +require('vendor/autoload.php'); + +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; + +abstract class TestCase extends PHPUnit_Framework_TestCase +{ + + /** + * Gets test's input and expected output. + * + * @param string $name + * + * @return array + */ + public function getData($name) + { + $input = file_get_contents('tests/data/' . $name . '.in'); + $output = unserialize(file_get_contents('tests/data/' . $name . '.out')); + return array($input, $output); + } + + /** + * Tests the `Lexer`. + * + * @param string $name + * + * @return Lexer + */ + public function runLexerTest($name) + { + list($input, $output) = $this->getData($name); + + $lexer = new Lexer($input); + $lexer->lex(); + + $this->assertEquals($output, $lexer); + + return $lexer; + } + + /** + * Tests the `Parser`. + * + * @param string $name + * + * @return Parser + */ + public function runParserTest($name) + { + list($input, $output) = $this->getData($name); + + $lexer = new Lexer($input); + $lexer->lex(); + + $parser = new Parser($lexer->tokens); + $parser->parse(); + + $this->assertEquals($output, $parser); + + return $parser; + } +} diff --git a/tests/data/lex.in b/tests/data/lex.in new file mode 100644 index 0000000..675e193 --- /dev/null +++ b/tests/data/lex.in @@ -0,0 +1 @@ +SELECT \\
\ No newline at end of file diff --git a/tests/data/lex.out b/tests/data/lex.out Binary files differnew file mode 100644 index 0000000..c0d4ba2 --- /dev/null +++ b/tests/data/lex.out diff --git a/tests/data/lexBool.in b/tests/data/lexBool.in new file mode 100644 index 0000000..9cad582 --- /dev/null +++ b/tests/data/lexBool.in @@ -0,0 +1 @@ +SELECT true, FalSe
\ No newline at end of file diff --git a/tests/data/lexBool.out b/tests/data/lexBool.out new file mode 100644 index 0000000..38ce22b --- /dev/null +++ b/tests/data/lexBool.out @@ -0,0 +1 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:18:"SELECT true, FalSe";s:3:"len";i:18;s:4:"last";i:18;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:7:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"true";s:5:"value";b:1;s:4:"type";i:5;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:11;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:12;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"FalSe";s:5:"value";b:0;s:4:"type";i:5;s:5:"flags";i:1;s:8:"position";i:13;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:7;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexComment.in b/tests/data/lexComment.in new file mode 100644 index 0000000..961b250 --- /dev/null +++ b/tests/data/lexComment.in @@ -0,0 +1,4 @@ +# comment +SELECT /*! STRAIGHT_JOIN */ col1 FROM table1, table2 /* select query */ +-- comment +-- comment 2
\ No newline at end of file diff --git a/tests/data/lexComment.out b/tests/data/lexComment.out new file mode 100644 index 0000000..52ceeff --- /dev/null +++ b/tests/data/lexComment.out @@ -0,0 +1,9 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:105:"# comment +SELECT /*! STRAIGHT_JOIN */ col1 FROM table1, table2 /* select query */ +-- comment +-- comment 2";s:3:"len";i:105;s:4:"last";i:106;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:19:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"# comment +";s:5:"value";s:10:"# comment +";s:4:"type";i:4;s:5:"flags";i:1;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:10;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:20:"/*! STRAIGHT_JOIN */";s:5:"value";s:20:"/*! STRAIGHT_JOIN */";s:4:"type";i:4;s:5:"flags";i:10;s:8:"position";i:17;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:37;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"col1";s:5:"value";s:4:"col1";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:38;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:43;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"table1";s:5:"value";s:6:"table1";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:48;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:54;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:55;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"table2";s:5:"value";s:6:"table2";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:56;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:62;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:18:"/* select query */";s:5:"value";s:18:"/* select query */";s:4:"type";i:4;s:5:"flags";i:2;s:8:"position";i:63;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:81;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:11:"-- comment +";s:5:"value";s:11:"-- comment +";s:4:"type";i:4;s:5:"flags";i:4;s:8:"position";i:82;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:12:"-- comment 2";s:5:"value";s:12:"-- comment 2";s:4:"type";i:4;s:5:"flags";i:4;s:8:"position";i:93;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:19;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexDelimiter.in b/tests/data/lexDelimiter.in new file mode 100644 index 0000000..b5c99ed --- /dev/null +++ b/tests/data/lexDelimiter.in @@ -0,0 +1,3 @@ +DELIMITER GO +SELECT a,b FROM foo GO +SELECT * FROM bar
\ No newline at end of file diff --git a/tests/data/lexDelimiter.out b/tests/data/lexDelimiter.out new file mode 100644 index 0000000..e92fa75 --- /dev/null +++ b/tests/data/lexDelimiter.out @@ -0,0 +1,5 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:53:"DELIMITER GO +SELECT a,b FROM foo GO +SELECT * FROM bar";s:3:"len";i:53;s:4:"last";i:53;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:24:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:9:"DELIMITER";s:5:"value";s:9:"DELIMITER";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:9;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"GO";s:5:"value";s:2:"GO";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:10;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:12;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:13;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:19;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"a";s:5:"value";s:1:"a";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:20;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:21;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"b";s:5:"value";s:1:"b";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:22;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:23;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:24;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:28;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"foo";s:5:"value";s:3:"foo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:29;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:32;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"GO";s:5:"value";s:2:"GO";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:33;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:36;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"*";s:5:"value";s:1:"*";s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:43;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:44;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:45;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:49;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"bar";s:5:"value";s:3:"bar";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:50;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:24;s:3:"idx";i:0;}s:9:"delimiter";s:2:"GO";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexKeyword.in b/tests/data/lexKeyword.in new file mode 100644 index 0000000..7a8f142 --- /dev/null +++ b/tests/data/lexKeyword.in @@ -0,0 +1 @@ +SELECT 1
\ No newline at end of file diff --git a/tests/data/lexKeyword.out b/tests/data/lexKeyword.out new file mode 100644 index 0000000..4322b28 --- /dev/null +++ b/tests/data/lexKeyword.out @@ -0,0 +1 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:8:"SELECT 1";s:3:"len";i:8;s:4:"last";i:8;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:4:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:4;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexNumber.in b/tests/data/lexNumber.in new file mode 100644 index 0000000..23c5ea6 --- /dev/null +++ b/tests/data/lexNumber.in @@ -0,0 +1,3 @@ +SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, 0XFfA, 1e-10, 1e10, .5e10; +-- invalid number +SELECT 12ex10;
\ No newline at end of file diff --git a/tests/data/lexNumber.out b/tests/data/lexNumber.out new file mode 100644 index 0000000..47e5c8d --- /dev/null +++ b/tests/data/lexNumber.out @@ -0,0 +1,6 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:121:"SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, 0XFfA, 1e-10, 1e10, .5e10; +-- invalid number +SELECT 12ex10;";s:3:"len";i:121;s:4:"last";i:121;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:50:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"12";s:5:"value";i:12;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:9;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:10;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"34";s:5:"value";i:34;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:11;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:13;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:14;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"5.67";s:5:"value";d:5.6699999999999999;s:4:"type";i:6;s:5:"flags";i:2;s:8:"position";i:15;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:19;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:20;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"0x89";s:5:"value";i:137;s:4:"type";i:6;s:5:"flags";i:1;s:8:"position";i:21;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:25;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:26;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"-10";s:5:"value";i:-10;s:4:"type";i:6;s:5:"flags";i:8;s:8:"position";i:27;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:30;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:31;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"--11";s:5:"value";i:11;s:4:"type";i:6;s:5:"flags";i:8;s:8:"position";i:32;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:36;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:37;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"+12";s:5:"value";i:12;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:38;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:41;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:3:".15";s:5:"value";d:0.14999999999999999;s:4:"type";i:6;s:5:"flags";i:2;s:8:"position";i:43;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:46;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"0xFFa";s:5:"value";i:4090;s:4:"type";i:6;s:5:"flags";i:1;s:8:"position";i:48;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:53;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:54;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"0xfFA";s:5:"value";i:4090;s:4:"type";i:6;s:5:"flags";i:1;s:8:"position";i:55;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:60;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:61;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"0XFfA";s:5:"value";i:4090;s:4:"type";i:6;s:5:"flags";i:1;s:8:"position";i:62;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:67;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:68;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"1e-10";s:5:"value";d:1.0E-10;s:4:"type";i:6;s:5:"flags";i:4;s:8:"position";i:69;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:74;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:75;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"1e10";s:5:"value";d:10000000000;s:4:"type";i:6;s:5:"flags";i:4;s:8:"position";i:76;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:80;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:81;}i:41;O:15:"SqlParser\Token":5:{s:5:"token";s:5:".5e10";s:5:"value";d:5000000000;s:4:"type";i:6;s:5:"flags";i:6;s:8:"position";i:82;}i:42;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:87;}i:43;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:88;}i:44;O:15:"SqlParser\Token":5:{s:5:"token";s:18:"-- invalid number +";s:5:"value";s:18:"-- invalid number +";s:4:"type";i:4;s:5:"flags";i:4;s:8:"position";i:89;}i:45;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:107;}i:46;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:113;}i:47;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"12ex10";s:5:"value";s:6:"12ex10";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:114;}i:48;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:120;}i:49;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:50;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexOperator.in b/tests/data/lexOperator.in new file mode 100644 index 0000000..ab544dd --- /dev/null +++ b/tests/data/lexOperator.in @@ -0,0 +1 @@ +SELECT 1 + 2
\ No newline at end of file diff --git a/tests/data/lexOperator.out b/tests/data/lexOperator.out new file mode 100644 index 0000000..d177511 --- /dev/null +++ b/tests/data/lexOperator.out @@ -0,0 +1 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:12:"SELECT 1 + 2";s:3:"len";i:12;s:4:"last";i:12;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:8:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"+";s:5:"value";s:1:"+";s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:9;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:10;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:11;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:8;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexString.in b/tests/data/lexString.in new file mode 100644 index 0000000..1aa9f71 --- /dev/null +++ b/tests/data/lexString.in @@ -0,0 +1 @@ +SELECT 'foo', "bar", "foo\\ bar"
\ No newline at end of file diff --git a/tests/data/lexString.out b/tests/data/lexString.out new file mode 100644 index 0000000..9eff57b --- /dev/null +++ b/tests/data/lexString.out @@ -0,0 +1 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:32:"SELECT 'foo', "bar", "foo\\ bar"";s:3:"len";i:32;s:4:"last";i:32;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:10:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"'foo'";s:5:"value";s:3:"foo";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:12;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:13;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:""bar"";s:5:"value";s:3:"bar";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:14;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:19;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:20;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:11:""foo\\ bar"";s:5:"value";s:9:"foo\\ bar";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:21;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:10;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexStringErr1.in b/tests/data/lexStringErr1.in new file mode 100644 index 0000000..861a0b8 --- /dev/null +++ b/tests/data/lexStringErr1.in @@ -0,0 +1 @@ +SELECT 'foo', "bar", "foo\\ bar
\ No newline at end of file diff --git a/tests/data/lexStringErr1.out b/tests/data/lexStringErr1.out Binary files differnew file mode 100644 index 0000000..eef71f2 --- /dev/null +++ b/tests/data/lexStringErr1.out diff --git a/tests/data/lexSymbol.in b/tests/data/lexSymbol.in new file mode 100644 index 0000000..c70205a --- /dev/null +++ b/tests/data/lexSymbol.in @@ -0,0 +1,2 @@ +SET @idx := 1; +SELECT @idx, @`idx`, @'idx'
\ No newline at end of file diff --git a/tests/data/lexSymbol.out b/tests/data/lexSymbol.out new file mode 100644 index 0000000..ad2c523 --- /dev/null +++ b/tests/data/lexSymbol.out @@ -0,0 +1,3 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:42:"SET @idx := 1; +SELECT @idx, @`idx`, @'idx'";s:3:"len";i:42;s:4:"last";i:42;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:19:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"SET";s:5:"value";s:3:"SET";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:3;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"@idx";s:5:"value";s:3:"idx";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:4;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:2:":=";s:5:"value";s:2:":=";s:4:"type";i:2;s:5:"flags";i:8;s:8:"position";i:9;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:11;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:12;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:13;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:14;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:15;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:21;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"@idx";s:5:"value";s:3:"idx";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:22;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:26;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:27;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"@`idx`";s:5:"value";s:3:"idx";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:28;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:34;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"@'idx'";s:5:"value";s:5:"'idx'";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:36;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:19;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/lexSymbolErr1.in b/tests/data/lexSymbolErr1.in new file mode 100644 index 0000000..f61f8ad --- /dev/null +++ b/tests/data/lexSymbolErr1.in @@ -0,0 +1,2 @@ +SET @idx := 1; +SELECT @idx, @`idx`, @'idx
\ No newline at end of file diff --git a/tests/data/lexSymbolErr1.out b/tests/data/lexSymbolErr1.out Binary files differnew file mode 100644 index 0000000..bd34306 --- /dev/null +++ b/tests/data/lexSymbolErr1.out diff --git a/tests/data/lexSymbolErr2.in b/tests/data/lexSymbolErr2.in new file mode 100644 index 0000000..ceb51ab --- /dev/null +++ b/tests/data/lexSymbolErr2.in @@ -0,0 +1,2 @@ +SET @idx := 1; +SELECT @idx, @`idx`, @
\ No newline at end of file diff --git a/tests/data/lexSymbolErr2.out b/tests/data/lexSymbolErr2.out Binary files differnew file mode 100644 index 0000000..fc40ba2 --- /dev/null +++ b/tests/data/lexSymbolErr2.out diff --git a/tests/data/lexSymbolErr3.in b/tests/data/lexSymbolErr3.in new file mode 100644 index 0000000..20bddac --- /dev/null +++ b/tests/data/lexSymbolErr3.in @@ -0,0 +1 @@ +SELECT `idx
\ No newline at end of file diff --git a/tests/data/lexSymbolErr3.out b/tests/data/lexSymbolErr3.out Binary files differnew file mode 100644 index 0000000..7dba4e6 --- /dev/null +++ b/tests/data/lexSymbolErr3.out diff --git a/tests/data/lexWhitespace.in b/tests/data/lexWhitespace.in new file mode 100644 index 0000000..a66a814 --- /dev/null +++ b/tests/data/lexWhitespace.in @@ -0,0 +1,10 @@ + + + +SELECT + 'w h i t e s p a c e' + + + + +
\ No newline at end of file diff --git a/tests/data/lexWhitespace.out b/tests/data/lexWhitespace.out new file mode 100644 index 0000000..adc713c --- /dev/null +++ b/tests/data/lexWhitespace.out @@ -0,0 +1,19 @@ +O:15:"SqlParser\Lexer":7:{s:6:"strict";b:0;s:3:"str";s:79:" + + +SELECT + 'w h i t e s p a c e' + + + + + ";s:3:"len";i:79;s:4:"last";i:79;s:6:"tokens";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:6:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:3:" + + +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:3;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:9:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:9;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:22:"'w h i t e s p a c e'";s:5:"value";s:20:"w h i t e s p a c e";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:18;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:39:" + + + + + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:40;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:6;s:3:"idx";i:0;}s:9:"delimiter";s:1:";";s:6:"errors";a:0:{}}
\ No newline at end of file diff --git a/tests/data/parse.in b/tests/data/parse.in new file mode 100644 index 0000000..027b7d6 --- /dev/null +++ b/tests/data/parse.in @@ -0,0 +1 @@ +SELECT 1;
\ No newline at end of file diff --git a/tests/data/parse.out b/tests/data/parse.out new file mode 100644 index 0000000..7bf1795 --- /dev/null +++ b/tests/data/parse.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:5:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:5;s:3:"idx";i:5;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\SelectStatement":11:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}s:4:"expr";a:1:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:1:"1";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:16;}}}s:4:"from";N;s:9:"partition";N;s:5:"where";N;s:5:"group";N;s:6:"having";N;s:5:"order";N;s:5:"limit";N;s:5:"first";N;s:4:"last";i:2;}}}
\ No newline at end of file diff --git a/tests/data/parseArrayErr1.in b/tests/data/parseArrayErr1.in new file mode 100644 index 0000000..af9acdd --- /dev/null +++ b/tests/data/parseArrayErr1.in @@ -0,0 +1 @@ +SELECT * FROM foo PARTITION bar, baz);
\ No newline at end of file diff --git a/tests/data/parseArrayErr1.out b/tests/data/parseArrayErr1.out Binary files differnew file mode 100644 index 0000000..df17613 --- /dev/null +++ b/tests/data/parseArrayErr1.out diff --git a/tests/data/parseArrayErr2.in b/tests/data/parseArrayErr2.in new file mode 100644 index 0000000..b29b07a --- /dev/null +++ b/tests/data/parseArrayErr2.in @@ -0,0 +1 @@ +SELECT * FROM foo PARTITION (bar baz) WHERE id > 0;
\ No newline at end of file diff --git a/tests/data/parseArrayErr2.out b/tests/data/parseArrayErr2.out Binary files differnew file mode 100644 index 0000000..2b9fe09 --- /dev/null +++ b/tests/data/parseArrayErr2.out diff --git a/tests/data/parseArrayErr3.in b/tests/data/parseArrayErr3.in new file mode 100644 index 0000000..128a77a --- /dev/null +++ b/tests/data/parseArrayErr3.in @@ -0,0 +1 @@ +SELECT * FROM foo PARTITION (bar, baz;
\ No newline at end of file diff --git a/tests/data/parseArrayErr3.out b/tests/data/parseArrayErr3.out new file mode 100644 index 0000000..67bdcc6 --- /dev/null +++ b/tests/data/parseArrayErr3.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:17:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"*";s:5:"value";s:1:"*";s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:9;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:13;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"foo";s:5:"value";s:3:"foo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:14;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:17;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:9:"PARTITION";s:5:"value";s:9:"PARTITION";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:18;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:27;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:28;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"bar";s:5:"value";s:3:"bar";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:29;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:32;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:33;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"baz";s:5:"value";s:3:"baz";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:34;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:37;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:17;s:3:"idx";i:17;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\SelectStatement":11:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}s:4:"expr";a:1:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:2:"* ";s:5:"alias";N;s:6:"tokens";a:2:{i:0;r:16;i:1;r:22;}}}s:4:"from";a:1:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";s:3:"foo";s:6:"column";N;s:4:"expr";s:3:"foo";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:40;}}}s:9:"partition";O:33:"SqlParser\Fragments\ArrayFragment":2:{s:5:"array";a:2:{i:0;s:3:"bar";i:1;s:3:"baz";}s:6:"tokens";a:2:{i:0;r:70;i:1;r:88;}}s:5:"where";N;s:5:"group";N;s:6:"having";N;s:5:"order";N;s:5:"limit";N;s:5:"first";N;s:4:"last";i:15;}}}
\ No newline at end of file diff --git a/tests/data/parseCall.in b/tests/data/parseCall.in new file mode 100644 index 0000000..871e666 --- /dev/null +++ b/tests/data/parseCall.in @@ -0,0 +1 @@ +CALL foo();
\ No newline at end of file diff --git a/tests/data/parseCall.out b/tests/data/parseCall.out new file mode 100644 index 0000000..2eeeaac --- /dev/null +++ b/tests/data/parseCall.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:7:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"CALL";s:5:"value";s:4:"CALL";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:4;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"foo";s:5:"value";s:3:"foo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:5;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:9;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:10;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:7;s:3:"idx";i:7;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:34:"SqlParser\Statements\CallStatement":3:{s:4:"call";O:31:"SqlParser\Fragments\CallKeyword":3:{s:4:"name";s:3:"foo";s:10:"parameters";a:0:{}s:6:"tokens";a:1:{i:0;r:16;}}s:5:"first";N;s:4:"last";i:4;}}}
\ No newline at end of file diff --git a/tests/data/parseCall2.in b/tests/data/parseCall2.in new file mode 100644 index 0000000..76fd802 --- /dev/null +++ b/tests/data/parseCall2.in @@ -0,0 +1 @@ +CALL foo(@bar, @baz);
\ No newline at end of file diff --git a/tests/data/parseCall2.out b/tests/data/parseCall2.out new file mode 100644 index 0000000..a581e74 --- /dev/null +++ b/tests/data/parseCall2.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:11:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"CALL";s:5:"value";s:4:"CALL";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:4;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"foo";s:5:"value";s:3:"foo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:5;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"@bar";s:5:"value";s:3:"bar";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:9;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:13;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:14;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"@baz";s:5:"value";s:3:"baz";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:15;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:19;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:20;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:11;s:3:"idx";i:11;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:34:"SqlParser\Statements\CallStatement":3:{s:4:"call";O:31:"SqlParser\Fragments\CallKeyword":3:{s:4:"name";s:3:"foo";s:10:"parameters";a:2:{i:0;s:3:"bar";i:1;s:3:"baz";}s:6:"tokens";a:3:{i:0;r:16;i:1;r:28;i:2;r:46;}}s:5:"first";N;s:4:"last";i:8;}}}
\ No newline at end of file diff --git a/tests/data/parseCreateFunction.in b/tests/data/parseCreateFunction.in new file mode 100644 index 0000000..95ce5f6 --- /dev/null +++ b/tests/data/parseCreateFunction.in @@ -0,0 +1,6 @@ +CREATE FUNCTION F_TEST(uid INT) RETURNS VARCHAR +BEGIN + DECLARE username VARCHAR DEFAULT ""; + SELECT username INTO username FROM users WHERE ID = uid; + RETURN username; +END
\ No newline at end of file diff --git a/tests/data/parseCreateFunction.out b/tests/data/parseCreateFunction.out new file mode 100644 index 0000000..44dab6c --- /dev/null +++ b/tests/data/parseCreateFunction.out @@ -0,0 +1,6 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:56:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"CREATE";s:5:"value";s:6:"CREATE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"FUNCTION";s:5:"value";s:8:"FUNCTION";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:15;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"F_TEST";s:5:"value";s:6:"F_TEST";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:16;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:22;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"uid";s:5:"value";s:3:"uid";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:23;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:26;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"INT";s:5:"value";s:3:"INT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:27;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:30;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:31;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"RETURNS";s:5:"value";s:7:"RETURNS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:32;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:39;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"VARCHAR";s:5:"value";s:7:"VARCHAR";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:40;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"BEGIN";s:5:"value";s:5:"BEGIN";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:48;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:53;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"DECLARE";s:5:"value";s:7:"DECLARE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:58;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:65;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:66;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:74;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"VARCHAR";s:5:"value";s:7:"VARCHAR";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:75;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:82;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"DEFAULT";s:5:"value";s:7:"DEFAULT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:83;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:90;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"""";s:5:"value";s:0:"";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:91;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:93;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:94;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:99;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:105;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:106;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:114;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"INTO";s:5:"value";s:4:"INTO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:115;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:119;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:120;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:128;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:129;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:133;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:134;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:139;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:140;}i:41;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:145;}i:42;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"ID";s:5:"value";s:2:"ID";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:146;}i:43;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:148;}i:44;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:149;}i:45;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:150;}i:46;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"uid";s:5:"value";s:3:"uid";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:151;}i:47;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:154;}i:48;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:155;}i:49;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"RETURN";s:5:"value";s:6:"RETURN";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:160;}i:50;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:166;}i:51;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:167;}i:52;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:175;}i:53;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:176;}i:54;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"END";s:5:"value";s:3:"END";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:177;}i:55;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:56;s:3:"idx";i:56;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\CreateStatement":10:{s:4:"name";O:37:"SqlParser\Fragments\CreateDefFragment":2:{s:4:"name";s:6:"F_TEST";s:6:"tokens";a:1:{i:0;r:28;}}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:8:"FUNCTION";}s:6:"tokens";a:1:{i:0;r:16;}}s:12:"tableOptions";N;s:6:"fields";N;s:4:"body";a:40:{i:0;r:94;i:1;r:100;i:2;r:106;i:3;r:112;i:4;r:118;i:5;r:124;i:6;r:130;i:7;r:136;i:8;r:142;i:9;r:148;i:10;r:154;i:11;r:160;i:12;r:166;i:13;r:172;i:14;r:178;i:15;r:184;i:16;r:190;i:17;r:196;i:18;r:202;i:19;r:208;i:20;r:214;i:21;r:220;i:22;r:226;i:23;r:232;i:24;r:238;i:25;r:244;i:26;r:250;i:27;r:256;i:28;r:262;i:29;r:268;i:30;r:274;i:31;r:280;i:32;r:286;i:33;r:292;i:34;r:298;i:35;r:304;i:36;r:310;i:37;r:316;i:38;r:322;i:39;r:328;}s:5:"first";N;s:4:"last";i:54;s:10:"parameters";a:1:{i:0;O:36:"SqlParser\Fragments\ParamDefFragment":4:{s:4:"name";s:3:"uid";s:5:"inOut";N;s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:3:"INT";s:4:"size";N;s:6:"tokens";a:1:{i:0;r:52;}}s:6:"tokens";a:2:{i:0;r:40;i:1;r:52;}}}s:6:"return";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:7:"VARCHAR";s:4:"size";N;s:6:"tokens";a:1:{i:0;r:82;}}s:11:"funcOptions";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}}}}
\ No newline at end of file diff --git a/tests/data/parseCreateFunctionErr1.in b/tests/data/parseCreateFunctionErr1.in new file mode 100644 index 0000000..5678b4e --- /dev/null +++ b/tests/data/parseCreateFunctionErr1.in @@ -0,0 +1,6 @@ +CREATE FUNCTION F_TEST(uid INT) +BEGIN + DECLARE username VARCHAR DEFAULT ""; + SELECT username INTO username FROM users WHERE ID = uid; + RETURN username; +END
\ No newline at end of file diff --git a/tests/data/parseCreateFunctionErr1.out b/tests/data/parseCreateFunctionErr1.out Binary files differnew file mode 100644 index 0000000..7e1c534 --- /dev/null +++ b/tests/data/parseCreateFunctionErr1.out diff --git a/tests/data/parseCreateProcedure.in b/tests/data/parseCreateProcedure.in new file mode 100644 index 0000000..b5e35c1 --- /dev/null +++ b/tests/data/parseCreateProcedure.in @@ -0,0 +1,4 @@ +CREATE PROCEDURE P_TEST(uid INT) +BEGIN + SELECT username FROM users WHERE ID = uid; +END
\ No newline at end of file diff --git a/tests/data/parseCreateProcedure.out b/tests/data/parseCreateProcedure.out new file mode 100644 index 0000000..86d7eb9 --- /dev/null +++ b/tests/data/parseCreateProcedure.out @@ -0,0 +1,4 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:32:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"CREATE";s:5:"value";s:6:"CREATE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:9:"PROCEDURE";s:5:"value";s:9:"PROCEDURE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"P_TEST";s:5:"value";s:6:"P_TEST";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:17;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:23;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"uid";s:5:"value";s:3:"uid";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:24;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:27;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"INT";s:5:"value";s:3:"INT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:28;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:31;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:32;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"BEGIN";s:5:"value";s:5:"BEGIN";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:33;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:38;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:43;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:49;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:50;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:58;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:59;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:63;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:64;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:69;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:70;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:75;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"ID";s:5:"value";s:2:"ID";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:76;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:78;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:79;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:80;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"uid";s:5:"value";s:3:"uid";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:81;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:84;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:85;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"END";s:5:"value";s:3:"END";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:86;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:32;s:3:"idx";i:32;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\CreateStatement":9:{s:4:"name";O:37:"SqlParser\Fragments\CreateDefFragment":2:{s:4:"name";s:6:"P_TEST";s:6:"tokens";a:1:{i:0;r:28;}}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:9:"PROCEDURE";}s:6:"tokens";a:1:{i:0;r:16;}}s:12:"tableOptions";N;s:6:"fields";N;s:4:"body";a:20:{i:0;r:70;i:1;r:76;i:2;r:82;i:3;r:88;i:4;r:94;i:5;r:100;i:6;r:106;i:7;r:112;i:8;r:118;i:9;r:124;i:10;r:130;i:11;r:136;i:12;r:142;i:13;r:148;i:14;r:154;i:15;r:160;i:16;r:166;i:17;r:172;i:18;r:178;i:19;r:184;}s:5:"first";N;s:4:"last";i:30;s:10:"parameters";a:1:{i:0;O:36:"SqlParser\Fragments\ParamDefFragment":4:{s:4:"name";s:3:"uid";s:5:"inOut";N;s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:3:"INT";s:4:"size";N;s:6:"tokens";a:1:{i:0;r:52;}}s:6:"tokens";a:2:{i:0;r:40;i:1;r:52;}}}s:11:"funcOptions";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}}}}
\ No newline at end of file diff --git a/tests/data/parseCreateProcedure2.in b/tests/data/parseCreateProcedure2.in new file mode 100644 index 0000000..e24b92e --- /dev/null +++ b/tests/data/parseCreateProcedure2.in @@ -0,0 +1,4 @@ +CREATE PROCEDURE P_TEST(IN uid INT, IN unused VARCHAR) +BEGIN + SELECT username FROM users WHERE ID = uid; +END
\ No newline at end of file diff --git a/tests/data/parseCreateProcedure2.out b/tests/data/parseCreateProcedure2.out new file mode 100644 index 0000000..0a3df50 --- /dev/null +++ b/tests/data/parseCreateProcedure2.out @@ -0,0 +1,4 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:41:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"CREATE";s:5:"value";s:6:"CREATE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:9:"PROCEDURE";s:5:"value";s:9:"PROCEDURE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"P_TEST";s:5:"value";s:6:"P_TEST";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:17;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:23;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"IN";s:5:"value";s:2:"IN";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:24;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:26;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"uid";s:5:"value";s:3:"uid";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:27;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:30;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"INT";s:5:"value";s:3:"INT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:31;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:34;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"IN";s:5:"value";s:2:"IN";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:36;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:38;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"unused";s:5:"value";s:6:"unused";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:39;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:45;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"VARCHAR";s:5:"value";s:7:"VARCHAR";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:46;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:53;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:54;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"BEGIN";s:5:"value";s:5:"BEGIN";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:55;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:60;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:65;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:71;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:72;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:80;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:81;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:85;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:86;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:91;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:92;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:97;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"ID";s:5:"value";s:2:"ID";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:98;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:100;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:101;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:102;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"uid";s:5:"value";s:3:"uid";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:103;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:106;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:107;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"END";s:5:"value";s:3:"END";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:108;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:41;s:3:"idx";i:41;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\CreateStatement":9:{s:4:"name";O:37:"SqlParser\Fragments\CreateDefFragment":2:{s:4:"name";s:6:"P_TEST";s:6:"tokens";a:1:{i:0;r:28;}}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:9:"PROCEDURE";}s:6:"tokens";a:1:{i:0;r:16;}}s:12:"tableOptions";N;s:6:"fields";N;s:4:"body";a:20:{i:0;r:124;i:1;r:130;i:2;r:136;i:3;r:142;i:4;r:148;i:5;r:154;i:6;r:160;i:7;r:166;i:8;r:172;i:9;r:178;i:10;r:184;i:11;r:190;i:12;r:196;i:13;r:202;i:14;r:208;i:15;r:214;i:16;r:220;i:17;r:226;i:18;r:232;i:19;r:238;}s:5:"first";N;s:4:"last";i:39;s:10:"parameters";a:2:{i:0;O:36:"SqlParser\Fragments\ParamDefFragment":4:{s:4:"name";s:3:"uid";s:5:"inOut";s:2:"IN";s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:3:"INT";s:4:"size";N;s:6:"tokens";a:1:{i:0;r:64;}}s:6:"tokens";a:3:{i:0;r:40;i:1;r:52;i:2;r:64;}}i:1;O:36:"SqlParser\Fragments\ParamDefFragment":4:{s:4:"name";s:6:"unused";s:5:"inOut";s:2:"IN";s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:7:"VARCHAR";s:4:"size";N;s:6:"tokens";a:1:{i:0;r:106;}}s:6:"tokens";a:3:{i:0;r:82;i:1;r:94;i:2;r:106;}}}s:11:"funcOptions";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}}}}
\ No newline at end of file diff --git a/tests/data/parseCreateTable.in b/tests/data/parseCreateTable.in new file mode 100644 index 0000000..e9f0911 --- /dev/null +++ b/tests/data/parseCreateTable.in @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS users ( + `id` INT NOT NULL AUTO_INCREMENT, + username VARCHAR(64) NULL, + `password` VARCHAR(256) DEFAULT '123456', + CONSTRAINT pk_id PRIMARY KEY (`id`), + UNIQUE (username) +) ENGINE=InnoDB;
\ No newline at end of file diff --git a/tests/data/parseCreateTable.out b/tests/data/parseCreateTable.out new file mode 100644 index 0000000..5708ee1 --- /dev/null +++ b/tests/data/parseCreateTable.out @@ -0,0 +1,7 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:65:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"CREATE";s:5:"value";s:6:"CREATE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"TABLE";s:5:"value";s:5:"TABLE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:12;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:13:"IF NOT EXISTS";s:5:"value";s:13:"IF NOT EXISTS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:13;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:26;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:27;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:32;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:33;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:34;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"`id`";s:5:"value";s:2:"id";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:39;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:43;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"INT";s:5:"value";s:3:"INT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:44;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"NOT NULL";s:5:"value";s:8:"NOT NULL";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:48;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:56;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:14:"AUTO_INCREMENT";s:5:"value";s:14:"AUTO_INCREMENT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:57;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:71;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:72;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:77;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:85;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"VARCHAR";s:5:"value";s:7:"VARCHAR";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:86;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:93;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"64";s:5:"value";i:64;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:94;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:96;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:97;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"NULL";s:5:"value";s:4:"NULL";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:98;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:102;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:103;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"`password`";s:5:"value";s:8:"password";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:108;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:118;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"VARCHAR";s:5:"value";s:7:"VARCHAR";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:119;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:126;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"256";s:5:"value";i:256;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:127;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:130;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:131;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"DEFAULT";s:5:"value";s:7:"DEFAULT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:132;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:139;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"'123456'";s:5:"value";s:6:"123456";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:140;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:148;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:149;}i:41;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"CONSTRAINT";s:5:"value";s:10:"CONSTRAINT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:154;}i:42;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:164;}i:43;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"pk_id";s:5:"value";s:5:"pk_id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:165;}i:44;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:170;}i:45;O:15:"SqlParser\Token":5:{s:5:"token";s:11:"PRIMARY KEY";s:5:"value";s:11:"PRIMARY KEY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:171;}i:46;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:182;}i:47;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:183;}i:48;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"`id`";s:5:"value";s:2:"id";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:184;}i:49;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:188;}i:50;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:189;}i:51;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:190;}i:52;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"UNIQUE";s:5:"value";s:6:"UNIQUE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:195;}i:53;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:201;}i:54;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:202;}i:55;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:203;}i:56;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:211;}i:57;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:212;}i:58;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:213;}i:59;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:214;}i:60;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"ENGINE";s:5:"value";s:6:"ENGINE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:215;}i:61;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:221;}i:62;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"InnoDB";s:5:"value";s:6:"InnoDB";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:222;}i:63;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:228;}i:64;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:65;s:3:"idx";i:65;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\CreateStatement":7:{s:4:"name";O:37:"SqlParser\Fragments\CreateDefFragment":2:{s:4:"name";s:5:"users";s:6:"tokens";a:1:{i:0;r:40;}}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:2:{i:1;s:5:"TABLE";i:3;s:13:"IF NOT EXISTS";}s:6:"tokens";a:2:{i:0;r:16;i:1;r:28;}}s:12:"tableOptions";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;a:2:{s:4:"name";s:6:"ENGINE";s:5:"value";s:6:"InnoDB";}}s:6:"tokens";a:3:{i:0;r:364;i:1;r:370;i:2;r:376;}}s:6:"fields";a:5:{i:0;O:36:"SqlParser\Fragments\FieldDefFragment":5:{s:4:"name";s:2:"id";s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:3:"INT";s:4:"size";N;s:6:"tokens";a:1:{i:0;r:76;}}s:7:"indexes";a:0:{}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:2:{i:1;s:8:"NOT NULL";i:3;s:14:"AUTO_INCREMENT";}s:6:"tokens";a:2:{i:0;r:88;i:1;r:100;}}s:6:"tokens";a:3:{i:0;r:64;i:1;r:76;i:2;r:88;}}i:1;O:36:"SqlParser\Fragments\FieldDefFragment":5:{s:4:"name";s:8:"username";s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:7:"VARCHAR";s:4:"size";a:1:{i:0;i:64;}s:6:"tokens";a:2:{i:0;r:130;i:1;r:142;}}s:7:"indexes";a:0:{}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:4:"NULL";}s:6:"tokens";a:1:{i:0;r:160;}}s:6:"tokens";a:3:{i:0;r:118;i:1;r:130;i:2;r:160;}}i:2;O:36:"SqlParser\Fragments\FieldDefFragment":5:{s:4:"name";s:8:"password";s:4:"type";O:36:"SqlParser\Fragments\DataTypeFragment":3:{s:4:"type";s:7:"VARCHAR";s:4:"size";a:1:{i:0;i:256;}s:6:"tokens";a:2:{i:0;r:190;i:1;r:202;}}s:7:"indexes";a:0:{}s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:2;a:2:{s:4:"name";s:7:"DEFAULT";s:5:"value";s:6:"123456";}}s:6:"tokens";a:2:{i:0;r:220;i:1;r:232;}}s:6:"tokens";a:3:{i:0;r:178;i:1;r:190;i:2;r:220;}}i:3;O:36:"SqlParser\Fragments\FieldDefFragment":5:{s:4:"name";s:5:"pk_id";s:4:"type";s:11:"PRIMARY KEY";s:7:"indexes";O:33:"SqlParser\Fragments\ArrayFragment":2:{s:5:"array";a:1:{i:0;s:2:"id";}s:6:"tokens";a:1:{i:0;r:292;}}s:7:"options";N;s:6:"tokens";a:4:{i:0;r:250;i:1;r:262;i:2;r:274;i:3;r:286;}}i:4;O:36:"SqlParser\Fragments\FieldDefFragment":5:{s:4:"name";N;s:4:"type";s:6:"UNIQUE";s:7:"indexes";O:33:"SqlParser\Fragments\ArrayFragment":2:{s:5:"array";a:1:{i:0;s:8:"username";}s:6:"tokens";a:1:{i:0;r:334;}}s:7:"options";N;s:6:"tokens";a:2:{i:0;r:316;i:1;r:328;}}}s:4:"body";N;s:5:"first";N;s:4:"last";i:62;}}}
\ No newline at end of file diff --git a/tests/data/parseDelete.in b/tests/data/parseDelete.in new file mode 100644 index 0000000..36aaf2d --- /dev/null +++ b/tests/data/parseDelete.in @@ -0,0 +1,7 @@ +DELETE LOW_PRIORITY +FROM + `test`.users +WHERE + `id`<3 AND (username="Dan" or username="Paul") +ORDER BY + id;
\ No newline at end of file diff --git a/tests/data/parseDelete.out b/tests/data/parseDelete.out new file mode 100644 index 0000000..9f4f7af --- /dev/null +++ b/tests/data/parseDelete.out @@ -0,0 +1,7 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:37:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"DELETE";s:5:"value";s:6:"DELETE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:12:"LOW_PRIORITY";s:5:"value";s:12:"LOW_PRIORITY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:19;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:20;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:24;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"`test`";s:5:"value";s:4:"test";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:29;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:35;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:36;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:41;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:42;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"`id`";s:5:"value";s:2:"id";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:52;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"<";s:5:"value";s:1:"<";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:56;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"3";s:5:"value";i:3;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:57;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:58;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"AND";s:5:"value";s:3:"AND";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:59;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:62;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:63;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:64;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:72;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:5:""Dan"";s:5:"value";s:3:"Dan";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:73;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:78;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"or";s:5:"value";s:2:"OR";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:79;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:81;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:82;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:90;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:6:""Paul"";s:5:"value";s:4:"Paul";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:91;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:97;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:98;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"ORDER";s:5:"value";s:5:"ORDER";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:99;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:104;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"BY";s:5:"value";s:2:"BY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:105;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:107;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:112;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:114;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:37;s:3:"idx";i:37;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\DeleteStatement":8:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:12:"LOW_PRIORITY";}s:6:"tokens";a:1:{i:0;r:16;}}s:4:"from";a:1:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";s:4:"test";s:5:"table";s:5:"users";s:6:"column";N;s:4:"expr";s:12:"`test`.users";s:5:"alias";N;s:6:"tokens";a:3:{i:0;r:40;i:1;r:46;i:2;r:52;}}}s:9:"partition";N;s:5:"where";a:7:{i:0;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:0;s:9:"condition";s:6:"`id`<3";s:6:"tokens";a:3:{i:0;r:76;i:1;r:82;i:2;r:88;}}i:1;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:1;s:9:"condition";s:3:"AND";s:6:"tokens";a:0:{}}i:2;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:1;s:9:"condition";s:1:"(";s:6:"tokens";a:0:{}}i:3;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:0;s:9:"condition";s:14:"username="Dan"";s:6:"tokens";a:3:{i:0;r:118;i:1;r:124;i:2;r:130;}}i:4;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:1;s:9:"condition";s:2:"OR";s:6:"tokens";a:0:{}}i:5;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:0;s:9:"condition";s:15:"username="Paul"";s:6:"tokens";a:3:{i:0;r:154;i:1;r:160;i:2;r:166;}}i:6;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:1;s:9:"condition";s:1:")";s:6:"tokens";a:0:{}}}s:5:"order";a:1:{i:0;O:32:"SqlParser\Fragments\OrderKeyword":3:{s:6:"column";s:2:"id";s:4:"type";s:3:"ASC";s:6:"tokens";a:1:{i:0;r:208;}}}s:5:"limit";N;s:5:"first";N;s:4:"last";i:34;}}}
\ No newline at end of file diff --git a/tests/data/parseInsert.in b/tests/data/parseInsert.in new file mode 100644 index 0000000..4f200e2 --- /dev/null +++ b/tests/data/parseInsert.in @@ -0,0 +1,6 @@ +INSERT LOW_PRIORITY +INTO + users(`id`, `username`, `password`) +VALUES + (1, "Dan", "5d41402abc4b2a76b9719d911017c592"), + (2, "Paul", "7d793037a0760186574b0282f2f435e7");
\ No newline at end of file diff --git a/tests/data/parseInsert.out b/tests/data/parseInsert.out new file mode 100644 index 0000000..5e8cbac --- /dev/null +++ b/tests/data/parseInsert.out @@ -0,0 +1,6 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:41:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"INSERT";s:5:"value";s:6:"INSERT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:12:"LOW_PRIORITY";s:5:"value";s:12:"LOW_PRIORITY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:19;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"INTO";s:5:"value";s:4:"INTO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:20;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:24;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:29;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:34;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"`id`";s:5:"value";s:2:"id";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:35;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:39;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:40;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"`username`";s:5:"value";s:8:"username";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:41;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:51;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:52;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"`password`";s:5:"value";s:8:"password";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:53;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:63;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:64;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"VALUES";s:5:"value";s:6:"VALUES";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:65;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:71;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:76;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:77;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:78;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:79;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:5:""Dan"";s:5:"value";s:3:"Dan";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:80;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:85;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:86;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:34:""5d41402abc4b2a76b9719d911017c592"";s:5:"value";s:32:"5d41402abc4b2a76b9719d911017c592";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:87;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:121;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:122;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:123;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:128;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:129;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:130;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:131;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:6:""Paul"";s:5:"value";s:4:"Paul";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:132;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:138;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:139;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:34:""7d793037a0760186574b0282f2f435e7"";s:5:"value";s:32:"7d793037a0760186574b0282f2f435e7";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:140;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:174;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:175;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:41;s:3:"idx";i:41;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\InsertStatement":5:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:12:"LOW_PRIORITY";}s:6:"tokens";a:1:{i:0;r:16;}}s:4:"into";O:31:"SqlParser\Fragments\IntoKeyword":3:{s:5:"table";s:5:"users";s:6:"fields";a:3:{i:0;s:2:"id";i:1;s:8:"username";i:2;s:8:"password";}s:6:"tokens";a:4:{i:0;r:40;i:1;r:52;i:2;r:70;i:3;r:88;}}s:6:"values";a:2:{i:0;O:33:"SqlParser\Fragments\ValuesKeyword":2:{s:6:"values";a:3:{i:0;s:1:"1";i:1;s:3:"Dan";i:2;s:32:"5d41402abc4b2a76b9719d911017c592";}s:6:"tokens";a:3:{i:0;r:124;i:1;r:142;i:2;r:160;}}i:1;O:33:"SqlParser\Fragments\ValuesKeyword":2:{s:6:"values";a:3:{i:0;s:1:"2";i:1;s:4:"Paul";i:2;s:32:"7d793037a0760186574b0282f2f435e7";}s:6:"tokens";a:3:{i:0;r:190;i:1;r:208;i:2;r:226;}}}s:5:"first";N;s:4:"last";i:38;}}}
\ No newline at end of file diff --git a/tests/data/parseRename.in b/tests/data/parseRename.in new file mode 100644 index 0000000..84ba79d --- /dev/null +++ b/tests/data/parseRename.in @@ -0,0 +1 @@ +RENAME TABLE foo TO bar;
\ No newline at end of file diff --git a/tests/data/parseRename.out b/tests/data/parseRename.out new file mode 100644 index 0000000..b15f045 --- /dev/null +++ b/tests/data/parseRename.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:11:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"RENAME";s:5:"value";s:6:"RENAME";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"TABLE";s:5:"value";s:5:"TABLE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:12;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"foo";s:5:"value";s:3:"foo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:13;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"TO";s:5:"value";s:2:"TO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:17;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:19;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"bar";s:5:"value";s:3:"bar";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:20;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:23;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:11;s:3:"idx";i:11;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\RenameStatement":3:{s:7:"renames";a:1:{i:0;O:33:"SqlParser\Fragments\RenameKeyword":3:{s:3:"old";s:3:"foo";s:3:"new";s:3:"bar";s:6:"tokens";a:2:{i:0;r:28;i:1;r:52;}}}s:5:"first";N;s:4:"last";i:8;}}}
\ No newline at end of file diff --git a/tests/data/parseRename2.in b/tests/data/parseRename2.in new file mode 100644 index 0000000..7adb641 --- /dev/null +++ b/tests/data/parseRename2.in @@ -0,0 +1 @@ +RENAME TABLE foo TO bar, baz TO qux;
\ No newline at end of file diff --git a/tests/data/parseRename2.out b/tests/data/parseRename2.out new file mode 100644 index 0000000..86055cc --- /dev/null +++ b/tests/data/parseRename2.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:18:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"RENAME";s:5:"value";s:6:"RENAME";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"TABLE";s:5:"value";s:5:"TABLE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:12;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"foo";s:5:"value";s:3:"foo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:13;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"TO";s:5:"value";s:2:"TO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:17;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:19;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"bar";s:5:"value";s:3:"bar";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:20;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:23;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:24;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"baz";s:5:"value";s:3:"baz";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:25;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:28;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"TO";s:5:"value";s:2:"TO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:29;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:31;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"qux";s:5:"value";s:3:"qux";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:32;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:35;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:18;s:3:"idx";i:18;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\RenameStatement":3:{s:7:"renames";a:2:{i:0;O:33:"SqlParser\Fragments\RenameKeyword":3:{s:3:"old";s:3:"foo";s:3:"new";s:3:"bar";s:6:"tokens";a:2:{i:0;r:28;i:1;r:52;}}i:1;O:33:"SqlParser\Fragments\RenameKeyword":3:{s:3:"old";s:3:"baz";s:3:"new";s:3:"qux";s:6:"tokens";a:2:{i:0;r:70;i:1;r:94;}}}s:5:"first";N;s:4:"last";i:15;}}}
\ No newline at end of file diff --git a/tests/data/parseReplace.in b/tests/data/parseReplace.in new file mode 100644 index 0000000..e2511b6 --- /dev/null +++ b/tests/data/parseReplace.in @@ -0,0 +1,3 @@ +REPLACE LOW_PRIORITY +INTO users(id, username) +VALUES (1, 'Foo'), (2, 'Bar')
\ No newline at end of file diff --git a/tests/data/parseReplace.out b/tests/data/parseReplace.out new file mode 100644 index 0000000..600303f --- /dev/null +++ b/tests/data/parseReplace.out @@ -0,0 +1,3 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:31:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"REPLACE";s:5:"value";s:7:"REPLACE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:7;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:12:"LOW_PRIORITY";s:5:"value";s:12:"LOW_PRIORITY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:8;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:20;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"INTO";s:5:"value";s:4:"INTO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:21;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:25;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:26;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:31;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:32;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:34;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:36;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:44;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:45;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"VALUES";s:5:"value";s:6:"VALUES";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:46;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:52;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:53;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:54;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:55;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:56;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"'Foo'";s:5:"value";s:3:"Foo";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:57;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:62;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:63;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:64;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:65;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:66;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:67;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:68;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"'Bar'";s:5:"value";s:3:"Bar";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:69;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:74;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:31;s:3:"idx";i:31;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:37:"SqlParser\Statements\ReplaceStatement":6:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:12:"LOW_PRIORITY";}s:6:"tokens";a:1:{i:0;r:16;}}s:4:"into";O:31:"SqlParser\Fragments\IntoKeyword":3:{s:5:"table";s:5:"users";s:6:"fields";a:2:{i:0;s:2:"id";i:1;s:8:"username";}s:6:"tokens";a:3:{i:0;r:40;i:1;r:52;i:2;r:70;}}s:6:"values";a:2:{i:0;O:33:"SqlParser\Fragments\ValuesKeyword":2:{s:6:"values";a:2:{i:0;s:1:"1";i:1;s:3:"Foo";}s:6:"tokens";a:2:{i:0;r:106;i:1;r:124;}}i:1;O:33:"SqlParser\Fragments\ValuesKeyword":2:{s:6:"values";a:2:{i:0;s:1:"2";i:1;s:3:"Bar";}s:6:"tokens";a:2:{i:0;r:154;i:1;r:172;}}}s:3:"set";N;s:5:"first";N;s:4:"last";i:29;}}}
\ No newline at end of file diff --git a/tests/data/parseReplace2.in b/tests/data/parseReplace2.in new file mode 100644 index 0000000..505c14f --- /dev/null +++ b/tests/data/parseReplace2.in @@ -0,0 +1,4 @@ +REPLACE LOW_PRIORITY +INTO users +SET id = 1, + username = 'Bar';
\ No newline at end of file diff --git a/tests/data/parseReplace2.out b/tests/data/parseReplace2.out new file mode 100644 index 0000000..330275f --- /dev/null +++ b/tests/data/parseReplace2.out @@ -0,0 +1,4 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:24:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"REPLACE";s:5:"value";s:7:"REPLACE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:7;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:12:"LOW_PRIORITY";s:5:"value";s:12:"LOW_PRIORITY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:8;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:20;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"INTO";s:5:"value";s:4:"INTO";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:21;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:25;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:26;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:31;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"SET";s:5:"value";s:3:"SET";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:32;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:36;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:38;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:39;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:40;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:41;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:42;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:43;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:48;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:56;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:57;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:58;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"'Bar'";s:5:"value";s:3:"Bar";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:59;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:64;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:24;s:3:"idx";i:24;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:37:"SqlParser\Statements\ReplaceStatement":6:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:1:{i:1;s:12:"LOW_PRIORITY";}s:6:"tokens";a:1:{i:0;r:16;}}s:4:"into";O:31:"SqlParser\Fragments\IntoKeyword":3:{s:5:"table";s:5:"users";s:6:"fields";N;s:6:"tokens";a:1:{i:0;r:40;}}s:6:"values";N;s:3:"set";a:2:{i:0;O:30:"SqlParser\Fragments\SetKeyword":3:{s:6:"column";s:2:"id";s:5:"value";i:1;s:6:"tokens";a:3:{i:0;r:64;i:1;r:76;i:2;r:88;}}i:1;O:30:"SqlParser\Fragments\SetKeyword":3:{s:6:"column";s:8:"username";s:5:"value";s:3:"Bar";s:6:"tokens";a:3:{i:0;r:106;i:1;r:118;i:2;r:130;}}}s:5:"first";N;s:4:"last";i:21;}}}
\ No newline at end of file diff --git a/tests/data/parseSelect.in b/tests/data/parseSelect.in new file mode 100644 index 0000000..e4d786e --- /dev/null +++ b/tests/data/parseSelect.in @@ -0,0 +1,14 @@ +SELECT ALL MAX_STATEMENT_TIME = 10 + 1 + 2 AS result, + @idx, + id, + test.`users`.username AS `name` +FROM + `test`.users, posts + PARTITION (p1, p2) +WHERE + id > 0 +ORDER BY + username DESC, + id +LIMIT 3 OFFSET 2;
\ No newline at end of file diff --git a/tests/data/parseSelect.out b/tests/data/parseSelect.out new file mode 100644 index 0000000..d98e16b --- /dev/null +++ b/tests/data/parseSelect.out @@ -0,0 +1,14 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:83:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"ALL";s:5:"value";s:3:"ALL";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:10;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:18:"MAX_STATEMENT_TIME";s:5:"value";s:18:"MAX_STATEMENT_TIME";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:11;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:29;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:30;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:31;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"10";s:5:"value";i:10;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:32;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:34;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:39;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:40;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"+";s:5:"value";s:1:"+";s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:41;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:43;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:44;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"AS";s:5:"value";s:2:"AS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:45;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"result";s:5:"value";s:6:"result";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:48;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:54;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:55;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"@idx";s:5:"value";s:3:"idx";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:60;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:64;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:65;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:70;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:72;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:73;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"test";s:5:"value";s:4:"test";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:78;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:82;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"`users`";s:5:"value";s:5:"users";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:83;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:90;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:91;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:99;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"AS";s:5:"value";s:2:"AS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:100;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:102;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"`name`";s:5:"value";s:4:"name";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:103;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:109;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:110;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:114;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"`test`";s:5:"value";s:4:"test";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:119;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:125;}i:41;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:126;}i:42;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:131;}i:43;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:132;}i:44;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"posts";s:5:"value";s:5:"posts";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:133;}i:45;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:138;}i:46;O:15:"SqlParser\Token":5:{s:5:"token";s:9:"PARTITION";s:5:"value";s:9:"PARTITION";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:143;}i:47;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:152;}i:48;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:153;}i:49;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"p1";s:5:"value";s:2:"p1";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:154;}i:50;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:156;}i:51;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:157;}i:52;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"p2";s:5:"value";s:2:"p2";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:158;}i:53;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:160;}i:54;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:161;}i:55;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:162;}i:56;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:167;}i:57;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:172;}i:58;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:174;}i:59;O:15:"SqlParser\Token":5:{s:5:"token";s:1:">";s:5:"value";s:1:">";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:175;}i:60;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:176;}i:61;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"0";s:5:"value";i:0;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:177;}i:62;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:178;}i:63;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"ORDER";s:5:"value";s:5:"ORDER";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:179;}i:64;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:184;}i:65;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"BY";s:5:"value";s:2:"BY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:185;}i:66;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:187;}i:67;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:192;}i:68;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:200;}i:69;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"DESC";s:5:"value";s:4:"DESC";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:201;}i:70;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:205;}i:71;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:206;}i:72;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:211;}i:73;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:213;}i:74;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"LIMIT";s:5:"value";s:5:"LIMIT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:214;}i:75;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:219;}i:76;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"3";s:5:"value";i:3;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:220;}i:77;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:221;}i:78;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"OFFSET";s:5:"value";s:6:"OFFSET";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:222;}i:79;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:228;}i:80;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:229;}i:81;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:230;}i:82;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:83;s:3:"idx";i:83;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\SelectStatement":11:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";a:2:{i:1;s:3:"ALL";i:3;a:2:{s:4:"name";s:18:"MAX_STATEMENT_TIME";s:5:"value";s:2:"10";}}s:6:"tokens";a:4:{i:0;r:16;i:1;r:28;i:2;r:40;i:3;r:52;}}s:4:"expr";a:4:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:6:"1 + 2 ";s:5:"alias";s:6:"result";s:6:"tokens";a:6:{i:0;r:64;i:1;r:70;i:2;r:76;i:3;r:82;i:4;r:88;i:5;r:94;}}i:1;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";s:3:"idx";s:4:"expr";s:4:"@idx";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:130;}}i:2;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";s:2:"id";s:4:"expr";s:2:"id";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:148;}}i:3;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";s:4:"test";s:5:"table";s:5:"users";s:6:"column";s:8:"username";s:4:"expr";s:21:"test.`users`.username";s:5:"alias";s:4:"name";s:6:"tokens";a:5:{i:0;r:166;i:1;r:172;i:2;r:178;i:3;r:184;i:4;r:190;}}}s:4:"from";a:2:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";s:4:"test";s:5:"table";s:5:"users";s:6:"column";N;s:4:"expr";s:12:"`test`.users";s:5:"alias";N;s:6:"tokens";a:3:{i:0;r:238;i:1;r:244;i:2;r:250;}}i:1;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";s:5:"posts";s:6:"column";N;s:4:"expr";s:5:"posts";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:268;}}}s:9:"partition";O:33:"SqlParser\Fragments\ArrayFragment":2:{s:5:"array";a:2:{i:0;s:2:"p1";i:1;s:2:"p2";}s:6:"tokens";a:2:{i:0;r:298;i:1;r:316;}}s:5:"where";a:1:{i:0;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:0;s:9:"condition";s:4:"id>0";s:6:"tokens";a:3:{i:0;r:346;i:1;r:358;i:2;r:370;}}}s:5:"group";N;s:6:"having";N;s:5:"order";a:2:{i:0;O:32:"SqlParser\Fragments\OrderKeyword":3:{s:6:"column";s:8:"username";s:4:"type";s:4:"DESC";s:6:"tokens";a:2:{i:0;r:406;i:1;r:418;}}i:1;O:32:"SqlParser\Fragments\OrderKeyword":3:{s:6:"column";s:2:"id";s:4:"type";s:3:"ASC";s:6:"tokens";a:1:{i:0;r:436;}}}s:5:"limit";O:32:"SqlParser\Fragments\LimitKeyword":3:{s:6:"offset";i:2;s:9:"row_count";i:3;s:6:"tokens";a:2:{i:0;r:460;i:1;r:484;}}s:5:"first";N;s:4:"last";i:80;}}}
\ No newline at end of file diff --git a/tests/data/parseSelectErr1.in b/tests/data/parseSelectErr1.in new file mode 100644 index 0000000..5140a74 --- /dev/null +++ b/tests/data/parseSelectErr1.in @@ -0,0 +1,14 @@ +SELECT ALL MAX_STATEMENT_TIME = 10 + 1 + 2 AS result, + @idx, + id, + test.`users`.username AS +FROM + `test`.users + PARTITION (p1, p2) +WHERE + id > 0 +ORDER BY + username DESC, + id +LIMIT 2, 3;
\ No newline at end of file diff --git a/tests/data/parseSelectErr1.out b/tests/data/parseSelectErr1.out new file mode 100644 index 0000000..4a129b6 --- /dev/null +++ b/tests/data/parseSelectErr1.out @@ -0,0 +1,14 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:77:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"ALL";s:5:"value";s:3:"ALL";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:10;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:18:"MAX_STATEMENT_TIME";s:5:"value";s:18:"MAX_STATEMENT_TIME";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:11;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:29;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:30;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:31;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"10";s:5:"value";i:10;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:32;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:34;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:39;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:40;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"+";s:5:"value";s:1:"+";s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:41;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:43;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:44;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"AS";s:5:"value";s:2:"AS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:45;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"result";s:5:"value";s:6:"result";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:48;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:54;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:55;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"@idx";s:5:"value";s:3:"idx";s:4:"type";i:8;s:5:"flags";i:1;s:8:"position";i:60;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:64;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:65;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:70;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:72;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:73;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"test";s:5:"value";s:4:"test";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:78;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:82;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:7:"`users`";s:5:"value";s:5:"users";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:83;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:90;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:91;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:99;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"AS";s:5:"value";s:2:"AS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:100;}i:34;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:102;}i:35;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:103;}i:36;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:107;}i:37;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"`test`";s:5:"value";s:4:"test";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:112;}i:38;O:15:"SqlParser\Token":5:{s:5:"token";s:1:".";s:5:"value";s:1:".";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:118;}i:39;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:119;}i:40;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:124;}i:41;O:15:"SqlParser\Token":5:{s:5:"token";s:9:"PARTITION";s:5:"value";s:9:"PARTITION";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:129;}i:42;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:138;}i:43;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:139;}i:44;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"p1";s:5:"value";s:2:"p1";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:140;}i:45;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:142;}i:46;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:143;}i:47;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"p2";s:5:"value";s:2:"p2";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:144;}i:48;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:146;}i:49;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:147;}i:50;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:148;}i:51;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:153;}i:52;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:158;}i:53;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:160;}i:54;O:15:"SqlParser\Token":5:{s:5:"token";s:1:">";s:5:"value";s:1:">";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:161;}i:55;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:162;}i:56;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"0";s:5:"value";i:0;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:163;}i:57;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:164;}i:58;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"ORDER";s:5:"value";s:5:"ORDER";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:165;}i:59;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:170;}i:60;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"BY";s:5:"value";s:2:"BY";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:171;}i:61;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:173;}i:62;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:178;}i:63;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:186;}i:64;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"DESC";s:5:"value";s:4:"DESC";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:187;}i:65;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:191;}i:66;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:192;}i:67;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:197;}i:68;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:199;}i:69;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"LIMIT";s:5:"value";s:5:"LIMIT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:200;}i:70;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:205;}i:71;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:206;}i:72;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:207;}i:73;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:208;}i:74;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"3";s:5:"value";i:3;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:209;}i:75;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:210;}i:76;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:77;s:3:"idx";i:77;}s:6:"strict";b:0;s:6:"errors";a:1:{i:0;O:36:"SqlParser\Exceptions\ParserException":8:{s:5:"token";r:214;s:10:" diff --git a/tests/data/parseSelectNested.in b/tests/data/parseSelectNested.in new file mode 100644 index 0000000..92afa74 --- /dev/null +++ b/tests/data/parseSelectNested.in @@ -0,0 +1 @@ +SELECT (SELECT 'foo') as Bar, (SELECT 'baz') as fOo;
\ No newline at end of file diff --git a/tests/data/parseSelectNested.out b/tests/data/parseSelectNested.out new file mode 100644 index 0000000..c5c2ead --- /dev/null +++ b/tests/data/parseSelectNested.out @@ -0,0 +1 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:24:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:8;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:14;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"'foo'";s:5:"value";s:3:"foo";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:15;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:20;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:21;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"as";s:5:"value";s:2:"AS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:22;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:24;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"Bar";s:5:"value";s:3:"Bar";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:25;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:28;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:29;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:30;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:31;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:37;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"'baz'";s:5:"value";s:3:"baz";s:4:"type";i:7;s:5:"flags";i:1;s:8:"position";i:38;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:43;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:44;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"as";s:5:"value";s:2:"AS";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:45;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:47;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"fOo";s:5:"value";s:3:"fOo";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:48;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:51;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:24;s:3:"idx";i:24;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\SelectStatement":11:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}s:4:"expr";a:2:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:15:"(SELECT 'foo') ";s:5:"alias";s:3:"Bar";s:6:"tokens";a:6:{i:0;r:16;i:1;r:22;i:2;r:28;i:3;r:34;i:4;r:40;i:5;r:46;}}i:1;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:15:"(SELECT 'baz') ";s:5:"alias";s:3:"fOo";s:6:"tokens";a:6:{i:0;r:82;i:1;r:88;i:2;r:94;i:3;r:100;i:4;r:106;i:5;r:112;}}}s:4:"from";N;s:9:"partition";N;s:5:"where";N;s:5:"group";N;s:6:"having";N;s:5:"order";N;s:5:"limit";N;s:5:"first";N;s:4:"last";i:21;}}}
\ No newline at end of file diff --git a/tests/data/parseUpdate.in b/tests/data/parseUpdate.in new file mode 100644 index 0000000..b35dbeb --- /dev/null +++ b/tests/data/parseUpdate.in @@ -0,0 +1,5 @@ +UPDATE + users +SET + username = "Dan", + id=155;
\ No newline at end of file diff --git a/tests/data/parseUpdate.out b/tests/data/parseUpdate.out new file mode 100644 index 0000000..2222cff --- /dev/null +++ b/tests/data/parseUpdate.out @@ -0,0 +1,5 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:18:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"UPDATE";s:5:"value";s:6:"UPDATE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:11;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"SET";s:5:"value";s:3:"SET";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:17;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:20;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:25;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:33;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:34;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:5:""Dan"";s:5:"value";s:3:"Dan";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:36;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:41;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:47;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:49;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"155";s:5:"value";i:155;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:50;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:53;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:18;s:3:"idx";i:18;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\UpdateStatement":8:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}s:4:"from";a:1:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";s:5:"users";s:6:"column";N;s:4:"expr";s:5:"users";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:16;}}}s:3:"set";a:2:{i:0;O:30:"SqlParser\Fragments\SetKeyword":3:{s:6:"column";s:8:"username";s:5:"value";s:3:"Dan";s:6:"tokens";a:3:{i:0;r:40;i:1;r:52;i:2;r:64;}}i:1;O:30:"SqlParser\Fragments\SetKeyword":3:{s:6:"column";s:2:"id";s:5:"value";i:155;s:6:"tokens";a:3:{i:0;r:82;i:1;r:88;i:2;r:94;}}}s:5:"where";N;s:5:"order";N;s:5:"limit";N;s:5:"first";N;s:4:"last";i:15;}}}
\ No newline at end of file diff --git a/tests/data/parseUpdate2.in b/tests/data/parseUpdate2.in new file mode 100644 index 0000000..7162118 --- /dev/null +++ b/tests/data/parseUpdate2.in @@ -0,0 +1,8 @@ +UPDATE + users +SET + username = "Dan", + id=155 +WHERE + username = "Paul" +LIMIT 1 OFFSET 2;
\ No newline at end of file diff --git a/tests/data/parseUpdate2.out b/tests/data/parseUpdate2.out new file mode 100644 index 0000000..c00163f --- /dev/null +++ b/tests/data/parseUpdate2.out @@ -0,0 +1,8 @@ +O:16:"SqlParser\Parser":4:{s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:34:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"UPDATE";s:5:"value";s:6:"UPDATE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"users";s:5:"value";s:5:"users";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:11;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:16;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"SET";s:5:"value";s:3:"SET";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:17;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:20;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:25;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:33;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:34;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:35;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:5:""Dan"";s:5:"value";s:3:"Dan";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:36;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:41;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:42;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"id";s:5:"value";s:2:"id";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:47;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:49;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"155";s:5:"value";i:155;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:50;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:53;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:54;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:5:" + ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:59;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"username";s:5:"value";s:8:"username";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:64;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:72;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:73;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:74;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:6:""Paul"";s:5:"value";s:4:"Paul";s:4:"type";i:7;s:5:"flags";i:2;s:8:"position";i:75;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" +";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:81;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"LIMIT";s:5:"value";s:5:"LIMIT";s:4:"type";i:1;s:5:"flags";i:0;s:8:"position";i:82;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:87;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:88;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:89;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"OFFSET";s:5:"value";s:6:"OFFSET";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:90;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:96;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"2";s:5:"value";i:2;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:97;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:98;}i:33;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:34;s:3:"idx";i:34;}s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\UpdateStatement":8:{s:7:"options";O:35:"SqlParser\Fragments\OptionsFragment":2:{s:7:"options";N;s:6:"tokens";a:0:{}}s:4:"from";a:1:{i:0;O:33:"SqlParser\Fragments\FieldFragment":6:{s:8:"database";N;s:5:"table";s:5:"users";s:6:"column";N;s:4:"expr";s:5:"users";s:5:"alias";N;s:6:"tokens";a:1:{i:0;r:16;}}}s:3:"set";a:2:{i:0;O:30:"SqlParser\Fragments\SetKeyword":3:{s:6:"column";s:8:"username";s:5:"value";s:3:"Dan";s:6:"tokens";a:3:{i:0;r:40;i:1;r:52;i:2;r:64;}}i:1;O:30:"SqlParser\Fragments\SetKeyword":3:{s:6:"column";s:2:"id";s:5:"value";i:155;s:6:"tokens";a:3:{i:0;r:82;i:1;r:88;i:2;r:94;}}}s:5:"where";a:1:{i:0;O:32:"SqlParser\Fragments\WhereKeyword":3:{s:10:"isOperator";b:0;s:9:"condition";s:15:"username="Paul"";s:6:"tokens";a:3:{i:0;r:118;i:1;r:130;i:2;r:142;}}}s:5:"order";N;s:5:"limit";O:32:"SqlParser\Fragments\LimitKeyword":3:{s:6:"offset";i:2;s:9:"row_count";i:1;s:6:"tokens";a:2:{i:0;r:166;i:1;r:190;}}s:5:"first";N;s:4:"last";i:31;}}}
\ No newline at end of file diff --git a/tests/lexer/IsMethodsTest.php b/tests/lexer/IsMethodsTest.php new file mode 100644 index 0000000..6121a4d --- /dev/null +++ b/tests/lexer/IsMethodsTest.php @@ -0,0 +1,123 @@ +<?php + +use SqlParser\Context; +use SqlParser\Token; + +class IsMethodsTest extends PHPUnit_Framework_TestCase +{ + + public function testIsKeyword() + { + $this->assertTrue(Context::isKeyword('SELECT')); + $this->assertTrue(Context::isKeyword('ALL')); + $this->assertTrue(Context::isKeyword('DISTINCT')); + $this->assertTrue(Context::isKeyword('FROM')); + + $this->assertTrue(Context::isKeyword('PRIMARY KEY')); + $this->assertTrue(Context::isKeyword('CHARACTER SET')); + + $this->assertFalse(Context::isKeyword('foo')); + $this->assertFalse(Context::isKeyword('bar baz')); + } + + public function testIsOperator() + { + $this->assertEquals(Token::FLAG_OPERATOR_ARITHMETIC, Context::isOperator('%')); + $this->assertEquals(Token::FLAG_OPERATOR_LOGICAL, Context::isOperator('!')); + $this->assertEquals(Token::FLAG_OPERATOR_LOGICAL, Context::isOperator('&&')); + $this->assertEquals(Token::FLAG_OPERATOR_LOGICAL, Context::isOperator('<=>')); + $this->assertEquals(Token::FLAG_OPERATOR_BITWISE, Context::isOperator('&')); + $this->assertEquals(Token::FLAG_OPERATOR_ASSIGNMENT, Context::isOperator(':=')); + $this->assertEquals(Token::FLAG_OPERATOR_SQL, Context::isOperator(',')); + + $this->assertEquals(Context::isOperator('a'), null); + } + + public function testIsWhitespace() + { + $this->assertTrue(Context::isWhitespace(" ")); + $this->assertTrue(Context::isWhitespace("\r")); + $this->assertTrue(Context::isWhitespace("\n")); + $this->assertTrue(Context::isWhitespace("\t")); + + $this->assertFalse(Context::isWhitespace("a")); + $this->assertFalse(Context::isWhitespace("\b")); + $this->assertFalse(Context::isWhitespace("\u1000")); + } + + public function testIsComment() + { + $this->assertEquals(Token::FLAG_COMMENT_BASH, Context::isComment('#')); + $this->assertEquals(Token::FLAG_COMMENT_C, Context::isComment('/*')); + $this->assertEquals(Token::FLAG_COMMENT_C, Context::isComment('*/')); + $this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment('-- ')); + $this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment("--\t")); + + $this->assertEquals(Token::FLAG_COMMENT_BASH, Context::isComment('# a comment')); + $this->assertEquals(Token::FLAG_COMMENT_C, Context::isComment('/*comment */')); + $this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment('-- my comment')); + + $this->assertEquals(Context::isComment("--\n"), null); + $this->assertEquals(Context::isComment('--not a comment'), null); + } + + public function testIsBool() + { + $this->assertTrue(Context::isBool('true')); + $this->assertTrue(Context::isBool('false')); + + $this->assertFalse(Context::isBool('tru')); + $this->assertFalse(Context::isBool('falsee')); + } + + public function testIsNumber() + { + $this->assertTrue(Context::isNumber('+')); + $this->assertTrue(Context::isNumber('-')); + $this->assertTrue(Context::isNumber('.')); + $this->assertTrue(Context::isNumber('0')); + $this->assertTrue(Context::isNumber('1')); + $this->assertTrue(Context::isNumber('2')); + $this->assertTrue(Context::isNumber('3')); + $this->assertTrue(Context::isNumber('4')); + $this->assertTrue(Context::isNumber('5')); + $this->assertTrue(Context::isNumber('6')); + $this->assertTrue(Context::isNumber('7')); + $this->assertTrue(Context::isNumber('8')); + $this->assertTrue(Context::isNumber('9')); + $this->assertTrue(Context::isNumber('e')); + $this->assertTrue(Context::isNumber('E')); + } + + public function testIsString() + { + $this->assertEquals(Token::FLAG_STRING_SINGLE_QUOTES, Context::isString("'")); + $this->assertEquals(Token::FLAG_STRING_DOUBLE_QUOTES, Context::isString('"')); + + $this->assertEquals(Token::FLAG_STRING_SINGLE_QUOTES, Context::isString("'foo bar'")); + $this->assertEquals(Token::FLAG_STRING_DOUBLE_QUOTES, Context::isString('"foo bar"')); + + $this->assertEquals(Context::isString('foo bar'), null); + } + + public function testIsSymbol() + { + $this->assertEquals(Token::FLAG_SYMBOL_VARIABLE, Context::isSymbol('@')); + $this->assertEquals(Token::FLAG_SYMBOL_BACKTICK, Context::isSymbol('`')); + + $this->assertEquals(Token::FLAG_SYMBOL_VARIABLE, Context::isSymbol('@id')); + $this->assertEquals(Token::FLAG_SYMBOL_BACKTICK, Context::isSymbol('`id`')); + + $this->assertEquals(Context::isSymbol('id'), null); + } + + public function testisSeparator() + { + $this->assertTrue(Context::isSeparator('+')); + $this->assertTrue(Context::isSeparator('.')); + + $this->assertFalse(Context::isSeparator('1')); + $this->assertFalse(Context::isSeparator('E')); + $this->assertFalse(Context::isSeparator('_')); + } +} diff --git a/tests/lexer/LexerTest.php b/tests/lexer/LexerTest.php new file mode 100644 index 0000000..bc0ede3 --- /dev/null +++ b/tests/lexer/LexerTest.php @@ -0,0 +1,104 @@ +<?php + +use SqlParser\Exceptions\LexerException; +use SqlParser\Lexer; + +class LexerTest extends TestCase +{ + + public function testError() + { + $lexer = new Lexer(''); + + $lexer->error('error #1', 'foo', 1, 2); + $lexer->error('error #2', 'bar', 3, 4); + + $this->assertEquals($lexer->errors, array( + new LexerException('error #1', 'foo', 1, 2), + new LexerException('error #2', 'bar', 3, 4), + )); + } + + /** + * @expectedException SqlParser\Exceptions\LexerException + * @expectedExceptionMessage strict error + * @expectedExceptionCode 4 + */ + public function testErrorStrict() + { + $lexer = new Lexer(''); + $lexer->strict = true; + + $lexer->error('strict error', 'foo', 1, 4); + } + + public function testLex() + { + $this->runLexerTest('lex'); + } + + public function testLexKeyword() + { + $this->runLexerTest('lexKeyword'); + } + + public function testLexOperator() + { + $this->runLexerTest('lexOperator'); + } + + public function testLexWhitespace() + { + $this->runLexerTest('lexWhitespace'); + } + + public function testLexComment() + { + $this->runLexerTest('lexComment'); + } + + public function testLexBool() + { + $this->runLexerTest('lexBool'); + } + + public function testLexNumber() + { + $this->runLexerTest('lexNumber'); + } + + public function testLexString() + { + $this->runLexerTest('lexString'); + } + + public function testLexStringErr1() + { + $this->runLexerTest('lexStringErr1'); + } + + public function testLexSymbol() + { + $this->runLexerTest('lexSymbol'); + } + + public function testLexSymbolErr1() + { + $this->runLexerTest('lexSymbolErr1'); + } + + public function testLexSymbolErr2() + { + $this->runLexerTest('lexSymbolErr2'); + } + + public function testLexSymbolErr3() + { + $this->runLexerTest('lexSymbolErr3'); + } + + public function testLexDelimiter() + { + $this->runLexerTest('lexDelimiter'); + } +} diff --git a/tests/lexer/TokenTest.php b/tests/lexer/TokenTest.php new file mode 100644 index 0000000..02e9ff1 --- /dev/null +++ b/tests/lexer/TokenTest.php @@ -0,0 +1,76 @@ +<?php + +use SqlParser\Token; + +class TokenTest extends PHPUnit_Framework_TestCase +{ + + public function testExtractKeyword() + { + $tok = new Token('SelecT', Token::TYPE_KEYWORD); + $this->assertEquals($tok->value, 'SELECT'); + + $tok = new Token('aS', Token::TYPE_KEYWORD); + $this->assertEquals($tok->value, 'AS'); + } + + public function testExtractWhitespace() + { + $tok = new Token(" \t \r \n ", Token::TYPE_WHITESPACE); + $this->assertEquals($tok->value, ' '); + } + + public function testExtractBool() + { + $tok = new Token('false', Token::TYPE_BOOL); + $this->assertFalse($tok->value); + + $tok = new Token('True', Token::TYPE_BOOL); + $this->assertTrue($tok->value); + } + + public function testExtractNumber() + { + $tok = new Token('--42', Token::TYPE_NUMBER, Token::FLAG_NUMBER_NEGATIVE); + $this->assertEquals($tok->value, 42); + + $tok = new Token('---42', Token::TYPE_NUMBER, Token::FLAG_NUMBER_NEGATIVE); + $this->assertEquals($tok->value, -42); + + $tok = new Token('0xFE', Token::TYPE_NUMBER, Token::FLAG_NUMBER_HEX); + $this->assertEquals($tok->value, 0xFE); + + $tok = new Token('-0xEF', Token::TYPE_NUMBER, Token::FLAG_NUMBER_NEGATIVE | Token::FLAG_NUMBER_HEX); + $this->assertEquals($tok->value, -0xEF); + + $tok = new Token('3.14', Token::TYPE_NUMBER, Token::FLAG_NUMBER_FLOAT); + $this->assertEquals($tok->value, 3.14); + } + + public function testExtractString() + { + $tok = new Token('"foo bar "', Token::TYPE_STRING); + $this->assertEquals($tok->value, 'foo bar '); + + $tok = new Token("' bar foo '", Token::TYPE_STRING); + $this->assertEquals($tok->value, ' bar foo '); + } + + public function testExtractSymbol() + { + $tok = new Token('@foo', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_VARIABLE); + $this->assertEquals($tok->value, 'foo'); + + $tok = new Token('`foo`', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_BACKTICK); + $this->assertEquals($tok->value, 'foo'); + + $tok = new Token('@`foo`', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_VARIABLE); + $this->assertEquals($tok->value, 'foo'); + } + + public function testInlineToken() + { + $token = new Token(" \r \n \t "); + $this->assertEquals($token->getInlineToken(), ' \r \n \t '); + } +} diff --git a/tests/lexer/TokensList.php b/tests/lexer/TokensList.php new file mode 100644 index 0000000..0415a8e --- /dev/null +++ b/tests/lexer/TokensList.php @@ -0,0 +1,103 @@ +<?php + +use SqlParser\Token; +use SqlParser\TokensList; + +class TokensListCase extends TestCase +{ + + /** + * Array of tokens that are used for testing. + * + * @var Token[] + */ + public $tokens; + + /** + * Constructor. + */ + public function __construct() + { + $this->tokens = array( + new Token('SELECT', Token::TYPE_KEYWORD), + new Token('*', Token::TYPE_OPERATOR), + new Token('FROM', Token::TYPE_KEYWORD), + new Token('`test`', Token::TYPE_SYMBOL) + ); + } + + /** + * Gets a list used for testing. + * + * @return TokensList + */ + public function getList() + { + $list = new TokensList(); + foreach ($this->tokens as $token) { + $list[] = $token; + } + return $list; + } + + public function testAdd() + { + $list = new TokensList(); + foreach ($this->tokens as $token) { + $list->add($token); + } + $this->assertEquals($this->getList(), $list); + } + + public function testGetNext() + { + $list = $this->getList(); + $this->assertEquals($this->tokens[0], $list->getNext()); + $this->assertEquals($this->tokens[1], $list->getNext()); + $this->assertEquals($this->tokens[2], $list->getNext()); + $this->assertEquals($this->tokens[3], $list->getNext()); + $this->assertEquals(null, $list->getNext()); + } + + public function testGetNextOfType() + { + $list = $this->getList(); + $this->assertEquals($this->tokens[0], $list->getNextOfType(Token::TYPE_KEYWORD)); + $this->assertEquals($this->tokens[2], $list->getNextOfType(Token::TYPE_KEYWORD)); + $this->assertEquals(null, $list->getNextOfType(Token::TYPE_KEYWORD)); + } + + public function testGetNextOfTypeAndValue() + { + $list = $this->getList(); + $this->assertEquals($this->tokens[0], $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'SELECT')); + $this->assertEquals(null, $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'SELECT')); + } + + public function testArrayAccess() + { + $list = new TokensList(); + + // offsetSet(NULL, $value) + foreach ($this->tokens as $token) { + $list[] = $token; + } + + // offsetSet($offset, $value) + $list[2] = $this->tokens[2]; + + // offsetGet($offset) + for ($i = 0, $count = count($this->tokens); $i < $count; ++$i) { + $this->assertEquals($this->tokens[$i], $list[$i]); + } + + // offsetExists($offset) + $this->assertTrue(isset($list[2])); + $this->assertFalse(isset($list[5])); + + // offsetUnset($offset) + unset($list[2]); + $this->assertEquals($this->tokens[3], $list[2]); + + } +} diff --git a/tests/parser/ArrayFragmentTest.php b/tests/parser/ArrayFragmentTest.php new file mode 100644 index 0000000..ad40385 --- /dev/null +++ b/tests/parser/ArrayFragmentTest.php @@ -0,0 +1,20 @@ +<?php + +class ArrayFragmentTest extends TestCase +{ + + public function testArrayErr1() + { + $this->runParserTest('parseArrayErr1'); + } + + public function testArrayErr2() + { + $this->runParserTest('parseArrayErr2'); + } + + public function testArrayErr3() + { + $this->runParserTest('parseArrayErr3'); + } +} diff --git a/tests/parser/CallStatementTest.php b/tests/parser/CallStatementTest.php new file mode 100644 index 0000000..c558ce1 --- /dev/null +++ b/tests/parser/CallStatementTest.php @@ -0,0 +1,15 @@ +<?php + +class CallStatementTest extends TestCase +{ + + public function testCall() + { + $this->runParserTest('parseCall'); + } + + public function testCall2() + { + $this->runParserTest('parseCall2'); + } +} diff --git a/tests/parser/CreateStatementTest.php b/tests/parser/CreateStatementTest.php new file mode 100644 index 0000000..b23e4ab --- /dev/null +++ b/tests/parser/CreateStatementTest.php @@ -0,0 +1,30 @@ +<?php + +class CreateStatementTest extends TestCase +{ + + public function testCreateTable() + { + $this->runParserTest('parseCreateTable'); + } + + public function testCreateProcedure() + { + $this->runParserTest('parseCreateProcedure'); + } + + public function testCreateProcedure2() + { + $this->runParserTest('parseCreateProcedure2'); + } + + public function testCreateFunction() + { + $this->runParserTest('parseCreateFunction'); + } + + public function testCreateFunctionErr1() + { + $this->runParserTest('parseCreateFunctionErr1'); + } +} diff --git a/tests/parser/DeleteStatementTest.php b/tests/parser/DeleteStatementTest.php new file mode 100644 index 0000000..dab28c7 --- /dev/null +++ b/tests/parser/DeleteStatementTest.php @@ -0,0 +1,10 @@ +<?php + +class DeleteStatementTest extends TestCase +{ + + public function testDelete() + { + $this->runParserTest('parseDelete'); + } +} diff --git a/tests/parser/InsertStatementTest.php b/tests/parser/InsertStatementTest.php new file mode 100644 index 0000000..bce6065 --- /dev/null +++ b/tests/parser/InsertStatementTest.php @@ -0,0 +1,10 @@ +<?php + +class InsertStatementTest extends TestCase +{ + + public function testInsert() + { + $this->runParserTest('parseInsert'); + } +} diff --git a/tests/parser/ParserTest.php b/tests/parser/ParserTest.php new file mode 100644 index 0000000..8a77086 --- /dev/null +++ b/tests/parser/ParserTest.php @@ -0,0 +1,66 @@ +<?php + +use SqlParser\Exceptions\ParserException; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +class ParserTest extends TestCase +{ + + public function testParse() + { + $this->runParserTest('parse'); + } + + public function testUnrecognizedStatement() + { + $lexer = new Lexer("SELECT 1; FROM"); + $lexer->lex(); + $parser = new Parser($lexer->tokens); + $parser->parse(); + $this->assertEquals( + $parser->errors[0]->getMessage(), + 'Unrecognized statement type "FROM".' + ); + } + + public function testUnrecognizedKeyword() + { + $lexer = new Lexer("SELECT 1 FROM foo PARTITION(bar, baz) AS"); + $lexer->lex(); + $parser = new Parser($lexer->tokens); + $parser->parse(); + $this->assertEquals( + $parser->errors[0]->getMessage(), + 'Unrecognized keyword "AS".' + ); + } + + public function testError() + { + $parser = new Parser(new TokensList()); + + $parser->error('error #1', new Token('foo'), 1); + $parser->error('error #2', new Token('bar'), 2); + + $this->assertEquals($parser->errors, array( + new ParserException('error #1', new Token('foo'), 1), + new ParserException('error #2', new Token('bar'), 2), + )); + } + + /** + * @expectedException SqlParser\Exceptions\ParserException + * @expectedExceptionMessage strict error + * @expectedExceptionCode 3 + */ + public function testErrorStrict() + { + $parser = new Parser(new TokensList()); + $parser->strict = true; + + $parser->error('strict error', new Token('foo'), 3); + } +} diff --git a/tests/parser/RenameStatementTest.php b/tests/parser/RenameStatementTest.php new file mode 100644 index 0000000..2ae608d --- /dev/null +++ b/tests/parser/RenameStatementTest.php @@ -0,0 +1,15 @@ +<?php + +class RenameStatementTest extends TestCase +{ + + public function testRename() + { + $this->runParserTest('parseRename'); + } + + public function testRename2() + { + $this->runParserTest('parseRename2'); + } +} diff --git a/tests/parser/ReplaceStatementTest.php b/tests/parser/ReplaceStatementTest.php new file mode 100644 index 0000000..4366a15 --- /dev/null +++ b/tests/parser/ReplaceStatementTest.php @@ -0,0 +1,15 @@ +<?php + +class ReplaceStatementTest extends TestCase +{ + + public function testReplace() + { + $this->runParserTest('parseReplace'); + } + + public function testReplace2() + { + $this->runParserTest('parseReplace2'); + } +} diff --git a/tests/parser/SelectStatementTest.php b/tests/parser/SelectStatementTest.php new file mode 100644 index 0000000..3ef76b2 --- /dev/null +++ b/tests/parser/SelectStatementTest.php @@ -0,0 +1,22 @@ +<?php + +class SelectStatementTest extends TestCase +{ + + public function testSelect() + { + $parser = $this->runParserTest('parseSelect'); + $stmt = $parser->statements[0]; + $this->assertEquals(10, $stmt->options->has('MAX_STATEMENT_TIME')); + } + + public function testSelectErr1() + { + $this->runParserTest('parseSelectErr1'); + } + + public function testSelectNested() + { + $this->runParserTest('parseSelectNested'); + } +} diff --git a/tests/parser/UpdateStatementTest.php b/tests/parser/UpdateStatementTest.php new file mode 100644 index 0000000..882d9ac --- /dev/null +++ b/tests/parser/UpdateStatementTest.php @@ -0,0 +1,15 @@ +<?php + +class UpdateStatementTest extends TestCase +{ + + public function testUpdate() + { + $this->runParserTest('parseUpdate'); + } + + public function testUpdate2() + { + $this->runParserTest('parseUpdate2'); + } +} |