├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── bin └── sql-formatter ├── composer.json ├── composer.lock ├── examples ├── cli.php └── examples.php ├── lib └── SqlFormatter.php ├── phpunit.xml.dist └── tests ├── SqlFormatterTest.php ├── clihighlight.html ├── compress.html ├── format-highlight.html ├── format.html ├── highlight.html ├── performance.php └── sql.sql /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.6 4 | - 5.5 5 | - 5.4 6 | - 5.3 7 | - hhvm 8 | 9 | script: phpunit --coverage-text 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Dorn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SqlFormatter 2 | ============= 3 | 4 | A lightweight php class for formatting sql statements. 5 | 6 | It can automatically indent and add line breaks in addition to syntax highlighting. 7 | 8 | History 9 | ============ 10 | 11 | I found myself having to debug auto-generated SQL statements all the time and 12 | wanted some way to easily output formatted HTML without having to include a 13 | huge library or copy and paste into online formatters. 14 | 15 | I was originally planning to extract the formatting code from PhpMyAdmin, 16 | but that was 10,000+ lines of code and used global variables. 17 | 18 | I saw that other people had the same problem and used Stack Overflow user 19 | losif's answer as a starting point. http://stackoverflow.com/a/3924147 20 | 21 | Usage 22 | ============ 23 | 24 | The SqlFormatter class has a static method 'format' which takes a SQL string 25 | as input and returns a formatted HTML block inside a pre tag. 26 | 27 | Sample usage: 28 | 29 | ```php 30 | = NOW()) ) 35 | GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10"; 36 | 37 | echo SqlFormatter::format($query); 38 | ``` 39 | 40 | Output: 41 | 42 | ![](http://jdorn.github.com/sql-formatter/format-highlight.png) 43 | 44 | Formatting Only 45 | ------------------------- 46 | If you don't want syntax highlighting and only want the indentations and 47 | line breaks, pass in false as the second parameter. 48 | 49 | This is useful for outputting to error logs or other non-html formats. 50 | 51 | ```php 52 | Run this PHP script from the command line to see CLI syntax highlighting and formatting. It supports Unix pipes or command line argument style.

"; 5 | echo "
php bin/sql-formatter \"SELECT * FROM MyTable WHERE (id>5 AND \\`name\\` LIKE \\"testing\\");\"
"; 6 | echo "
echo \"SELECT * FROM MyTable WHERE (id>5 AND \\`name\\` LIKE \\"testing\\");\" | php bin/sql-formatter
"; 7 | exit; 8 | } 9 | 10 | if(isset($argv[1])) { 11 | $sql = $argv[1]; 12 | } 13 | else { 14 | $sql = stream_get_contents(fopen("php://stdin", "r")); 15 | } 16 | 17 | require_once(__DIR__.'/../lib/SqlFormatter.php'); 18 | 19 | /** 20 | * Returns true if the stream supports colorization. 21 | * 22 | * Colorization is disabled if not supported by the stream: 23 | * 24 | * - Windows without Ansicon and ConEmu 25 | * - non tty consoles 26 | * 27 | * @return bool true if the stream supports colorization, false otherwise 28 | * @link https://github.com/symfony/symfony/blob/v2.4.6/src/Symfony/Component/Console/Output/StreamOutput.php#L97 29 | */ 30 | function hasColorSupport() { 31 | if (DIRECTORY_SEPARATOR == '\\') { 32 | return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); 33 | } 34 | 35 | return function_exists('posix_isatty') && @posix_isatty(STDOUT); 36 | } 37 | 38 | $highlight = hasColorSupport(); 39 | 40 | echo SqlFormatter::format($sql, $highlight); 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jdorn/sql-formatter", 3 | "description": "a PHP SQL highlighting library", 4 | "homepage": "https://github.com/jdorn/sql-formatter/", 5 | "keywords": ["sql", "highlight"], 6 | "minimum-stability": "dev", 7 | "license": "MIT", 8 | "type": "library", 9 | "require": { 10 | "php": ">=5.2.4" 11 | }, 12 | "require-dev": { 13 | "phpunit/phpunit": "3.7.*" 14 | }, 15 | "authors": [ 16 | { 17 | "name": "Jeremy Dorn", 18 | "email": "jeremy@jeremydorn.com", 19 | "homepage": "http://jeremydorn.com/" 20 | } 21 | ], 22 | "autoload": { 23 | "classmap": ["lib"] 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.3.x-dev" 28 | } 29 | }, 30 | "bin": ["bin/sql-formatter"] 31 | } 32 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "a709b40d4a35e7077aa40fbd0f78f6c6", 3 | "packages": [ 4 | 5 | ], 6 | "packages-dev": [ 7 | { 8 | "name": "phpunit/php-code-coverage", 9 | "version": "1.2.x-dev", 10 | "source": { 11 | "type": "git", 12 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 13 | "reference": "1.2.9" 14 | }, 15 | "dist": { 16 | "type": "zip", 17 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.9", 18 | "reference": "1.2.9", 19 | "shasum": "" 20 | }, 21 | "require": { 22 | "php": ">=5.3.3", 23 | "phpunit/php-file-iterator": ">=1.3.0@stable", 24 | "phpunit/php-text-template": ">=1.1.1@stable", 25 | "phpunit/php-token-stream": ">=1.1.3@stable" 26 | }, 27 | "suggest": { 28 | "ext-dom": "*", 29 | "ext-xdebug": ">=2.0.5" 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "classmap": [ 34 | "PHP/" 35 | ] 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "include-path": [ 39 | "" 40 | ], 41 | "license": [ 42 | "BSD-3-Clause" 43 | ], 44 | "authors": [ 45 | { 46 | "name": "Sebastian Bergmann", 47 | "email": "sb@sebastian-bergmann.de", 48 | "role": "lead" 49 | } 50 | ], 51 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 52 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 53 | "keywords": [ 54 | "coverage", 55 | "testing", 56 | "xunit" 57 | ], 58 | "time": "2013-02-26 18:55:56" 59 | }, 60 | { 61 | "name": "phpunit/php-file-iterator", 62 | "version": "dev-master", 63 | "source": { 64 | "type": "git", 65 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 66 | "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26" 67 | }, 68 | "dist": { 69 | "type": "zip", 70 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2deb24c65ea78e126daa8d45b2089ddc29ec1d26", 71 | "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26", 72 | "shasum": "" 73 | }, 74 | "require": { 75 | "php": ">=5.3.3" 76 | }, 77 | "type": "library", 78 | "autoload": { 79 | "classmap": [ 80 | "File/" 81 | ] 82 | }, 83 | "notification-url": "https://packagist.org/downloads/", 84 | "include-path": [ 85 | "" 86 | ], 87 | "license": [ 88 | "BSD-3-Clause" 89 | ], 90 | "authors": [ 91 | { 92 | "name": "Sebastian Bergmann", 93 | "email": "sb@sebastian-bergmann.de", 94 | "role": "lead" 95 | } 96 | ], 97 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 98 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 99 | "keywords": [ 100 | "filesystem", 101 | "iterator" 102 | ], 103 | "time": "2013-01-07 10:47:05" 104 | }, 105 | { 106 | "name": "phpunit/php-text-template", 107 | "version": "dev-master", 108 | "source": { 109 | "type": "git", 110 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 111 | "reference": "1eeef106193d2f8c539728e566bb4793071a9e18" 112 | }, 113 | "dist": { 114 | "type": "zip", 115 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/1eeef106193d2f8c539728e566bb4793071a9e18", 116 | "reference": "1eeef106193d2f8c539728e566bb4793071a9e18", 117 | "shasum": "" 118 | }, 119 | "require": { 120 | "php": ">=5.3.3" 121 | }, 122 | "type": "library", 123 | "autoload": { 124 | "classmap": [ 125 | "Text/" 126 | ] 127 | }, 128 | "notification-url": "https://packagist.org/downloads/", 129 | "include-path": [ 130 | "" 131 | ], 132 | "license": [ 133 | "BSD-3-Clause" 134 | ], 135 | "authors": [ 136 | { 137 | "name": "Sebastian Bergmann", 138 | "email": "sb@sebastian-bergmann.de", 139 | "role": "lead" 140 | } 141 | ], 142 | "description": "Simple template engine.", 143 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 144 | "keywords": [ 145 | "template" 146 | ], 147 | "time": "2013-01-07 10:56:17" 148 | }, 149 | { 150 | "name": "phpunit/php-timer", 151 | "version": "1.0.x-dev", 152 | "source": { 153 | "type": "git", 154 | "url": "https://github.com/sebastianbergmann/php-timer.git", 155 | "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3" 156 | }, 157 | "dist": { 158 | "type": "zip", 159 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/ecf7920b27003a9412b07dad79dbb5ad1249e6c3", 160 | "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3", 161 | "shasum": "" 162 | }, 163 | "require": { 164 | "php": ">=5.3.3" 165 | }, 166 | "type": "library", 167 | "autoload": { 168 | "classmap": [ 169 | "PHP/" 170 | ] 171 | }, 172 | "notification-url": "https://packagist.org/downloads/", 173 | "include-path": [ 174 | "" 175 | ], 176 | "license": [ 177 | "BSD-3-Clause" 178 | ], 179 | "authors": [ 180 | { 181 | "name": "Sebastian Bergmann", 182 | "email": "sb@sebastian-bergmann.de", 183 | "role": "lead" 184 | } 185 | ], 186 | "description": "Utility class for timing", 187 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 188 | "keywords": [ 189 | "timer" 190 | ], 191 | "time": "2013-01-30 06:08:51" 192 | }, 193 | { 194 | "name": "phpunit/php-token-stream", 195 | "version": "dev-master", 196 | "source": { 197 | "type": "git", 198 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 199 | "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98" 200 | }, 201 | "dist": { 202 | "type": "zip", 203 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c25dd88e1592e66dee2553c99ef244203d5a1b98", 204 | "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98", 205 | "shasum": "" 206 | }, 207 | "require": { 208 | "ext-tokenizer": "*", 209 | "php": ">=5.3.3" 210 | }, 211 | "type": "library", 212 | "autoload": { 213 | "classmap": [ 214 | "PHP/" 215 | ] 216 | }, 217 | "notification-url": "https://packagist.org/downloads/", 218 | "include-path": [ 219 | "" 220 | ], 221 | "license": [ 222 | "BSD-3-Clause" 223 | ], 224 | "authors": [ 225 | { 226 | "name": "Sebastian Bergmann", 227 | "email": "sb@sebastian-bergmann.de", 228 | "role": "lead" 229 | } 230 | ], 231 | "description": "Wrapper around PHP's tokenizer extension.", 232 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 233 | "keywords": [ 234 | "tokenizer" 235 | ], 236 | "time": "2013-01-07 10:56:35" 237 | }, 238 | { 239 | "name": "phpunit/phpunit", 240 | "version": "3.7.x-dev", 241 | "source": { 242 | "type": "git", 243 | "url": "https://github.com/sebastianbergmann/phpunit.git", 244 | "reference": "2c67e52445416bb7c14046b432acd7eb79e4e612" 245 | }, 246 | "dist": { 247 | "type": "zip", 248 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2c67e52445416bb7c14046b432acd7eb79e4e612", 249 | "reference": "2c67e52445416bb7c14046b432acd7eb79e4e612", 250 | "shasum": "" 251 | }, 252 | "require": { 253 | "ext-dom": "*", 254 | "ext-pcre": "*", 255 | "ext-reflection": "*", 256 | "ext-spl": "*", 257 | "php": ">=5.3.3", 258 | "phpunit/php-code-coverage": ">=1.2.1,<1.3.0", 259 | "phpunit/php-file-iterator": ">=1.3.1", 260 | "phpunit/php-text-template": ">=1.1.1", 261 | "phpunit/php-timer": ">=1.0.2,<1.1.0", 262 | "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", 263 | "symfony/yaml": ">=2.2.0" 264 | }, 265 | "require-dev": { 266 | "pear-pear/pear": "1.9.4" 267 | }, 268 | "suggest": { 269 | "ext-json": "*", 270 | "ext-simplexml": "*", 271 | "ext-tokenizer": "*", 272 | "phpunit/php-invoker": ">=1.1.0,<1.2.0" 273 | }, 274 | "bin": [ 275 | "composer/bin/phpunit" 276 | ], 277 | "type": "library", 278 | "extra": { 279 | "branch-alias": { 280 | "dev-master": "3.7.x-dev" 281 | } 282 | }, 283 | "autoload": { 284 | "classmap": [ 285 | "PHPUnit/" 286 | ] 287 | }, 288 | "notification-url": "https://packagist.org/downloads/", 289 | "include-path": [ 290 | "", 291 | "../../symfony/yaml/" 292 | ], 293 | "license": [ 294 | "BSD-3-Clause" 295 | ], 296 | "authors": [ 297 | { 298 | "name": "Sebastian Bergmann", 299 | "email": "sebastian@phpunit.de", 300 | "role": "lead" 301 | } 302 | ], 303 | "description": "The PHP Unit Testing framework.", 304 | "homepage": "http://www.phpunit.de/", 305 | "keywords": [ 306 | "phpunit", 307 | "testing", 308 | "xunit" 309 | ], 310 | "time": "2013-03-11 07:06:05" 311 | }, 312 | { 313 | "name": "phpunit/phpunit-mock-objects", 314 | "version": "1.2.x-dev", 315 | "source": { 316 | "type": "git", 317 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 318 | "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4" 319 | }, 320 | "dist": { 321 | "type": "zip", 322 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/d49b5683200b5db9b1c64cb06f52f50d147891c4", 323 | "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4", 324 | "shasum": "" 325 | }, 326 | "require": { 327 | "php": ">=5.3.3", 328 | "phpunit/php-text-template": ">=1.1.1@stable" 329 | }, 330 | "suggest": { 331 | "ext-soap": "*" 332 | }, 333 | "type": "library", 334 | "autoload": { 335 | "classmap": [ 336 | "PHPUnit/" 337 | ] 338 | }, 339 | "notification-url": "https://packagist.org/downloads/", 340 | "include-path": [ 341 | "" 342 | ], 343 | "license": [ 344 | "BSD-3-Clause" 345 | ], 346 | "authors": [ 347 | { 348 | "name": "Sebastian Bergmann", 349 | "email": "sb@sebastian-bergmann.de", 350 | "role": "lead" 351 | } 352 | ], 353 | "description": "Mock Object library for PHPUnit", 354 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 355 | "keywords": [ 356 | "mock", 357 | "xunit" 358 | ], 359 | "time": "2013-02-05 07:46:41" 360 | }, 361 | { 362 | "name": "symfony/yaml", 363 | "version": "dev-master", 364 | "target-dir": "Symfony/Component/Yaml", 365 | "source": { 366 | "type": "git", 367 | "url": "https://github.com/symfony/Yaml.git", 368 | "reference": "f198ac28048eeceae852419c076123aaee59cd1c" 369 | }, 370 | "dist": { 371 | "type": "zip", 372 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/f198ac28048eeceae852419c076123aaee59cd1c", 373 | "reference": "f198ac28048eeceae852419c076123aaee59cd1c", 374 | "shasum": "" 375 | }, 376 | "require": { 377 | "php": ">=5.3.3" 378 | }, 379 | "type": "library", 380 | "extra": { 381 | "branch-alias": { 382 | "dev-master": "2.3-dev" 383 | } 384 | }, 385 | "autoload": { 386 | "psr-0": { 387 | "Symfony\\Component\\Yaml\\": "" 388 | } 389 | }, 390 | "notification-url": "https://packagist.org/downloads/", 391 | "license": [ 392 | "MIT" 393 | ], 394 | "authors": [ 395 | { 396 | "name": "Fabien Potencier", 397 | "email": "fabien@symfony.com" 398 | }, 399 | { 400 | "name": "Symfony Community", 401 | "homepage": "http://symfony.com/contributors" 402 | } 403 | ], 404 | "description": "Symfony Yaml Component", 405 | "homepage": "http://symfony.com", 406 | "time": "2013-01-31 21:39:01" 407 | } 408 | ], 409 | "aliases": [ 410 | 411 | ], 412 | "minimum-stability": "dev", 413 | "stability-flags": [ 414 | 415 | ], 416 | "platform": { 417 | "php": ">=5.2.4" 418 | }, 419 | "platform-dev": [ 420 | 421 | ] 422 | } 423 | -------------------------------------------------------------------------------- /examples/cli.php: -------------------------------------------------------------------------------- 1 | Run this php script from the command line to see CLI syntax highlighting and formatting. It support Unix pipes or command line argument style.

"; 4 | echo "
php examples/cli.php \"SELECT * FROM MyTable WHERE (id>5 AND \\`name\\` LIKE \\"testing\\");\"
"; 5 | echo "
echo \"SELECT * FROM MyTable WHERE (id>5 AND \\`name\\` LIKE \\"testing\\");\" | php examples/cli.php
"; 6 | } 7 | 8 | if(isset($argv[1])) { 9 | $sql = $argv[1]; 10 | } 11 | else { 12 | $sql = stream_get_contents(fopen("php://stdin", "r")); 13 | } 14 | 15 | require_once(__DIR__.'/../lib/SqlFormatter.php'); 16 | 17 | echo SqlFormatter::format($sql); 18 | -------------------------------------------------------------------------------- /examples/examples.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SqlFormatter Examples 5 | 30 | 31 | 32 | = 43 | DATE_FORMAT((DATE_SUB(NOW(),INTERVAL 1 DAY)),'%Y-%c-%d') AND t_create 44 | < DATE_FORMAT(NOW(), '%Y-%c-%d') ORDER BY d.id LIMIT 2,10) a, 45 | orc_scheme_detail b WHERE a.id = b.id", 46 | 47 | "SELECT * from Table1 LEFT 48 | OUTER JOIN Table2 on Table1.id = Table2.id", 49 | 50 | "SELECT * FROM MyTable WHERE id = 46", 51 | 52 | "SELECT count(*),`Column1` as count,`Testing`, `Testing Three` FROM `Table1` 53 | WHERE Column1 = 'testing' AND ( (`Column2` = `Column3` OR Column4 >= NOW()) ) 54 | GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10", 55 | 56 | "select * from `Table`, (SELECT group_concat(column1) as col FROM Table2 GROUP BY category) 57 | Table2, Table3 where Table2.col = (Table3.col2 - `Table`.id)", 58 | 59 | "insert ignore into Table3 (column1, column2) VALUES ('test1','test2'), ('test3','test4');", 60 | 61 | "UPDATE MyTable SET name='sql', category='databases' WHERE id > '65'", 62 | 63 | "delete from MyTable WHERE name LIKE \"test%\"", 64 | 65 | "SELECT * FROM UnmatchedParens WHERE ( A = B)) AND (((Test=1)", 66 | 67 | "-- This is a comment 68 | SELECT 69 | /* This is another comment 70 | On more than one line */ 71 | Id #This is one final comment 72 | as temp, DateCreated as Created FROM MyTable;", 73 | ); 74 | 75 | // Example statements for splitting SQL strings into individual queries 76 | $split_statements = array( 77 | "DROP TABLE IF EXISTS MyTable; 78 | CREATE TABLE MyTable ( id int ); 79 | INSERT INTO MyTable (id) 80 | VALUES 81 | (1),(2),(3),(4); 82 | SELECT * FROM MyTable;", 83 | 84 | "SELECT \";\"; SELECT \";\\\"; a;\"; 85 | SELECT \"; 86 | abc\"; 87 | SELECT a,b #comment; 88 | FROM test;", 89 | 90 | " 91 | -- Drop the table first if it exists 92 | DROP TABLE IF EXISTS MyTable; 93 | 94 | -- Create the table 95 | CREATE TABLE MyTable ( id int ); 96 | 97 | -- Insert values 98 | INSERT INTO MyTable (id) 99 | VALUES 100 | (1),(2),(3),(4); 101 | 102 | -- Done", 103 | ); 104 | 105 | // Example statements for removing comments 106 | $comment_statements = array( 107 | "-- This is a comment 108 | SELECT 109 | /* This is another comment 110 | On more than one line */ 111 | Id #This is one final comment 112 | as temp, DateCreated as Created FROM MyTable;", 113 | ); 114 | ?> 115 | 116 | 117 |

Formatting And Syntax Highlighting

118 | 119 |
120 | Usage: 121 |
122 |     '); ?>
123 |     
124 |
125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 135 | 136 | 137 | 138 |
OriginalFormatted And Highlighted
133 |
134 |
139 | 140 | 141 |

Formatting Only

142 | 143 |
144 | Usage: 145 |
146 |     '); ?>
147 |     
148 |
149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 159 | 160 | 161 | 162 |
OriginalFormatted
157 |
158 |
163 | 164 | 165 |

Syntax Highlighting Only

166 | 167 |
168 | Usage: 169 |
170 |     '); ?>
171 |     
172 |
173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 183 | 184 | 185 | 186 |
OriginalHighlighted
181 |
182 |
187 | 188 | 189 |

Compress Query

190 | 191 |
192 | Usage: 193 |
194 |     '); ?>
195 |     
196 |
197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 207 | 208 | 209 | 210 |
OriginalCompressed
205 |
206 |
211 | 212 | 213 |

Splitting SQL Strings Into Individual Queries

214 | 215 |
216 | Usage: 217 |
218 |     '); ?>
219 |     
220 |
221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 231 | 239 | 240 | 241 |
OriginalSplit
229 |
230 |
"; 234 | foreach ($queries as $query) { 235 | echo "
  • " . SqlFormatter::highlight($query) . "
  • "; 236 | } 237 | echo ""; 238 | ?>
    242 | 243 | 244 |

    Removing Comments

    245 | 246 |
    247 | Usage: 248 |
    249 |     '); ?>
    250 |     
    251 |
    252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 262 | 265 | 266 | 267 |
    OriginalComments Removed
    260 |
    261 |
    263 |
    264 |
    268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /lib/SqlFormatter.php: -------------------------------------------------------------------------------- 1 | 8 | * @author Florin Patan 9 | * @copyright 2013 Jeremy Dorn 10 | * @license http://opensource.org/licenses/MIT 11 | * @link http://github.com/jdorn/sql-formatter 12 | * @version 1.2.18 13 | */ 14 | class SqlFormatter 15 | { 16 | // Constants for token types 17 | const TOKEN_TYPE_WHITESPACE = 0; 18 | const TOKEN_TYPE_WORD = 1; 19 | const TOKEN_TYPE_QUOTE = 2; 20 | const TOKEN_TYPE_BACKTICK_QUOTE = 3; 21 | const TOKEN_TYPE_RESERVED = 4; 22 | const TOKEN_TYPE_RESERVED_TOPLEVEL = 5; 23 | const TOKEN_TYPE_RESERVED_NEWLINE = 6; 24 | const TOKEN_TYPE_BOUNDARY = 7; 25 | const TOKEN_TYPE_COMMENT = 8; 26 | const TOKEN_TYPE_BLOCK_COMMENT = 9; 27 | const TOKEN_TYPE_NUMBER = 10; 28 | const TOKEN_TYPE_ERROR = 11; 29 | const TOKEN_TYPE_VARIABLE = 12; 30 | 31 | // Constants for different components of a token 32 | const TOKEN_TYPE = 0; 33 | const TOKEN_VALUE = 1; 34 | 35 | // Reserved words (for syntax highlighting) 36 | protected static $reserved = array( 37 | 'ACCESSIBLE', 'ACTION', 'AGAINST', 'AGGREGATE', 'ALGORITHM', 'ALL', 'ALTER', 'ANALYSE', 'ANALYZE', 'AS', 'ASC', 38 | 'AUTOCOMMIT', 'AUTO_INCREMENT', 'BACKUP', 'BEGIN', 'BETWEEN', 'BINLOG', 'BOTH', 'CASCADE', 'CASE', 'CHANGE', 'CHANGED', 'CHARACTER SET', 39 | 'CHARSET', 'CHECK', 'CHECKSUM', 'COLLATE', 'COLLATION', 'COLUMN', 'COLUMNS', 'COMMENT', 'COMMIT', 'COMMITTED', 'COMPRESSED', 'CONCURRENT', 40 | 'CONSTRAINT', 'CONTAINS', 'CONVERT', 'CREATE', 'CROSS', 'CURRENT_TIMESTAMP', 'DATABASE', 'DATABASES', 'DAY', 'DAY_HOUR', 'DAY_MINUTE', 41 | 'DAY_SECOND', 'DEFAULT', 'DEFINER', 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DETERMINISTIC', 'DISTINCT', 'DISTINCTROW', 'DIV', 42 | 'DO', 'DUMPFILE', 'DUPLICATE', 'DYNAMIC', 'ELSE', 'ENCLOSED', 'END', 'ENGINE', 'ENGINE_TYPE', 'ENGINES', 'ESCAPE', 'ESCAPED', 'EVENTS', 'EXEC', 43 | 'EXECUTE', 'EXISTS', 'EXPLAIN', 'EXTENDED', 'FAST', 'FIELDS', 'FILE', 'FIRST', 'FIXED', 'FLUSH', 'FOR', 'FORCE', 'FOREIGN', 'FULL', 'FULLTEXT', 44 | 'FUNCTION', 'GLOBAL', 'GRANT', 'GRANTS', 'GROUP_CONCAT', 'HEAP', 'HIGH_PRIORITY', 'HOSTS', 'HOUR', 'HOUR_MINUTE', 45 | 'HOUR_SECOND', 'IDENTIFIED', 'IF', 'IFNULL', 'IGNORE', 'IN', 'INDEX', 'INDEXES', 'INFILE', 'INSERT', 'INSERT_ID', 'INSERT_METHOD', 'INTERVAL', 46 | 'INTO', 'INVOKER', 'IS', 'ISOLATION', 'KEY', 'KEYS', 'KILL', 'LAST_INSERT_ID', 'LEADING', 'LEVEL', 'LIKE', 'LINEAR', 47 | 'LINES', 'LOAD', 'LOCAL', 'LOCK', 'LOCKS', 'LOGS', 'LOW_PRIORITY', 'MARIA', 'MASTER', 'MASTER_CONNECT_RETRY', 'MASTER_HOST', 'MASTER_LOG_FILE', 48 | 'MATCH','MAX_CONNECTIONS_PER_HOUR', 'MAX_QUERIES_PER_HOUR', 'MAX_ROWS', 'MAX_UPDATES_PER_HOUR', 'MAX_USER_CONNECTIONS', 49 | 'MEDIUM', 'MERGE', 'MINUTE', 'MINUTE_SECOND', 'MIN_ROWS', 'MODE', 'MODIFY', 50 | 'MONTH', 'MRG_MYISAM', 'MYISAM', 'NAMES', 'NATURAL', 'NOT', 'NOW()','NULL', 'OFFSET', 'ON', 'OPEN', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', 51 | 'ON UPDATE', 'ON DELETE', 'OUTFILE', 'PACK_KEYS', 'PAGE', 'PARTIAL', 'PARTITION', 'PARTITIONS', 'PASSWORD', 'PRIMARY', 'PRIVILEGES', 'PROCEDURE', 52 | 'PROCESS', 'PROCESSLIST', 'PURGE', 'QUICK', 'RANGE', 'RAID0', 'RAID_CHUNKS', 'RAID_CHUNKSIZE','RAID_TYPE', 'READ', 'READ_ONLY', 53 | 'READ_WRITE', 'REFERENCES', 'REGEXP', 'RELOAD', 'RENAME', 'REPAIR', 'REPEATABLE', 'REPLACE', 'REPLICATION', 'RESET', 'RESTORE', 'RESTRICT', 54 | 'RETURN', 'RETURNS', 'REVOKE', 'RLIKE', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', 'SECOND', 'SECURITY', 'SEPARATOR', 55 | 'SERIALIZABLE', 'SESSION', 'SHARE', 'SHOW', 'SHUTDOWN', 'SLAVE', 'SONAME', 'SOUNDS', 'SQL', 'SQL_AUTO_IS_NULL', 'SQL_BIG_RESULT', 56 | 'SQL_BIG_SELECTS', 'SQL_BIG_TABLES', 'SQL_BUFFER_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_LOG_BIN', 'SQL_LOG_OFF', 'SQL_LOG_UPDATE', 57 | 'SQL_LOW_PRIORITY_UPDATES', 'SQL_MAX_JOIN_SIZE', 'SQL_QUOTE_SHOW_CREATE', 'SQL_SAFE_UPDATES', 'SQL_SELECT_LIMIT', 'SQL_SLAVE_SKIP_COUNTER', 58 | 'SQL_SMALL_RESULT', 'SQL_WARNINGS', 'SQL_CACHE', 'SQL_NO_CACHE', 'START', 'STARTING', 'STATUS', 'STOP', 'STORAGE', 59 | 'STRAIGHT_JOIN', 'STRING', 'STRIPED', 'SUPER', 'TABLE', 'TABLES', 'TEMPORARY', 'TERMINATED', 'THEN', 'TO', 'TRAILING', 'TRANSACTIONAL', 'TRUE', 60 | 'TRUNCATE', 'TYPE', 'TYPES', 'UNCOMMITTED', 'UNIQUE', 'UNLOCK', 'UNSIGNED', 'USAGE', 'USE', 'USING', 'VARIABLES', 61 | 'VIEW', 'WHEN', 'WITH', 'WORK', 'WRITE', 'YEAR_MONTH' 62 | ); 63 | 64 | // For SQL formatting 65 | // These keywords will all be on their own line 66 | protected static $reserved_toplevel = array( 67 | 'SELECT', 'FROM', 'WHERE', 'SET', 'ORDER BY', 'GROUP BY', 'LIMIT', 'DROP', 68 | 'VALUES', 'UPDATE', 'HAVING', 'ADD', 'AFTER', 'ALTER TABLE', 'DELETE FROM', 'UNION ALL', 'UNION', 'EXCEPT', 'INTERSECT' 69 | ); 70 | 71 | protected static $reserved_newline = array( 72 | 'LEFT OUTER JOIN', 'RIGHT OUTER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'OUTER JOIN', 'INNER JOIN', 'JOIN', 'XOR', 'OR', 'AND' 73 | ); 74 | 75 | protected static $functions = array ( 76 | 'ABS', 'ACOS', 'ADDDATE', 'ADDTIME', 'AES_DECRYPT', 'AES_ENCRYPT', 'AREA', 'ASBINARY', 'ASCII', 'ASIN', 'ASTEXT', 'ATAN', 'ATAN2', 77 | 'AVG', 'BDMPOLYFROMTEXT', 'BDMPOLYFROMWKB', 'BDPOLYFROMTEXT', 'BDPOLYFROMWKB', 'BENCHMARK', 'BIN', 'BIT_AND', 'BIT_COUNT', 'BIT_LENGTH', 78 | 'BIT_OR', 'BIT_XOR', 'BOUNDARY', 'BUFFER', 'CAST', 'CEIL', 'CEILING', 'CENTROID', 'CHAR', 'CHARACTER_LENGTH', 'CHARSET', 'CHAR_LENGTH', 79 | 'COALESCE', 'COERCIBILITY', 'COLLATION', 'COMPRESS', 'CONCAT', 'CONCAT_WS', 'CONNECTION_ID', 'CONTAINS', 'CONV', 'CONVERT', 'CONVERT_TZ', 80 | 'CONVEXHULL', 'COS', 'COT', 'COUNT', 'CRC32', 'CROSSES', 'CURDATE', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER', 81 | 'CURTIME', 'DATABASE', 'DATE', 'DATEDIFF', 'DATE_ADD', 'DATE_DIFF', 'DATE_FORMAT', 'DATE_SUB', 'DAY', 'DAYNAME', 'DAYOFMONTH', 'DAYOFWEEK', 82 | 'DAYOFYEAR', 'DECODE', 'DEFAULT', 'DEGREES', 'DES_DECRYPT', 'DES_ENCRYPT', 'DIFFERENCE', 'DIMENSION', 'DISJOINT', 'DISTANCE', 'ELT', 'ENCODE', 83 | 'ENCRYPT', 'ENDPOINT', 'ENVELOPE', 'EQUALS', 'EXP', 'EXPORT_SET', 'EXTERIORRING', 'EXTRACT', 'EXTRACTVALUE', 'FIELD', 'FIND_IN_SET', 'FLOOR', 84 | 'FORMAT', 'FOUND_ROWS', 'FROM_DAYS', 'FROM_UNIXTIME', 'GEOMCOLLFROMTEXT', 'GEOMCOLLFROMWKB', 'GEOMETRYCOLLECTION', 'GEOMETRYCOLLECTIONFROMTEXT', 85 | 'GEOMETRYCOLLECTIONFROMWKB', 'GEOMETRYFROMTEXT', 'GEOMETRYFROMWKB', 'GEOMETRYN', 'GEOMETRYTYPE', 'GEOMFROMTEXT', 'GEOMFROMWKB', 'GET_FORMAT', 86 | 'GET_LOCK', 'GLENGTH', 'GREATEST', 'GROUP_CONCAT', 'GROUP_UNIQUE_USERS', 'HEX', 'HOUR', 'IF', 'IFNULL', 'INET_ATON', 'INET_NTOA', 'INSERT', 'INSTR', 87 | 'INTERIORRINGN', 'INTERSECTION', 'INTERSECTS', 'INTERVAL', 'ISCLOSED', 'ISEMPTY', 'ISNULL', 'ISRING', 'ISSIMPLE', 'IS_FREE_LOCK', 'IS_USED_LOCK', 88 | 'LAST_DAY', 'LAST_INSERT_ID', 'LCASE', 'LEAST', 'LEFT', 'LENGTH', 'LINEFROMTEXT', 'LINEFROMWKB', 'LINESTRING', 'LINESTRINGFROMTEXT', 'LINESTRINGFROMWKB', 89 | 'LN', 'LOAD_FILE', 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCATE', 'LOG', 'LOG10', 'LOG2', 'LOWER', 'LPAD', 'LTRIM', 'MAKEDATE', 'MAKETIME', 'MAKE_SET', 90 | 'MASTER_POS_WAIT', 'MAX', 'MBRCONTAINS', 'MBRDISJOINT', 'MBREQUAL', 'MBRINTERSECTS', 'MBROVERLAPS', 'MBRTOUCHES', 'MBRWITHIN', 'MD5', 'MICROSECOND', 91 | 'MID', 'MIN', 'MINUTE', 'MLINEFROMTEXT', 'MLINEFROMWKB', 'MOD', 'MONTH', 'MONTHNAME', 'MPOINTFROMTEXT', 'MPOINTFROMWKB', 'MPOLYFROMTEXT', 'MPOLYFROMWKB', 92 | 'MULTILINESTRING', 'MULTILINESTRINGFROMTEXT', 'MULTILINESTRINGFROMWKB', 'MULTIPOINT', 'MULTIPOINTFROMTEXT', 'MULTIPOINTFROMWKB', 'MULTIPOLYGON', 93 | 'MULTIPOLYGONFROMTEXT', 'MULTIPOLYGONFROMWKB', 'NAME_CONST', 'NULLIF', 'NUMGEOMETRIES', 'NUMINTERIORRINGS', 'NUMPOINTS', 'OCT', 'OCTET_LENGTH', 94 | 'OLD_PASSWORD', 'ORD', 'OVERLAPS', 'PASSWORD', 'PERIOD_ADD', 'PERIOD_DIFF', 'PI', 'POINT', 'POINTFROMTEXT', 'POINTFROMWKB', 'POINTN', 'POINTONSURFACE', 95 | 'POLYFROMTEXT', 'POLYFROMWKB', 'POLYGON', 'POLYGONFROMTEXT', 'POLYGONFROMWKB', 'POSITION', 'POW', 'POWER', 'QUARTER', 'QUOTE', 'RADIANS', 'RAND', 96 | 'RELATED', 'RELEASE_LOCK', 'REPEAT', 'REPLACE', 'REVERSE', 'RIGHT', 'ROUND', 'ROW_COUNT', 'RPAD', 'RTRIM', 'SCHEMA', 'SECOND', 'SEC_TO_TIME', 97 | 'SESSION_USER', 'SHA', 'SHA1', 'SIGN', 'SIN', 'SLEEP', 'SOUNDEX', 'SPACE', 'SQRT', 'SRID', 'STARTPOINT', 'STD', 'STDDEV', 'STDDEV_POP', 'STDDEV_SAMP', 98 | 'STRCMP', 'STR_TO_DATE', 'SUBDATE', 'SUBSTR', 'SUBSTRING', 'SUBSTRING_INDEX', 'SUBTIME', 'SUM', 'SYMDIFFERENCE', 'SYSDATE', 'SYSTEM_USER', 'TAN', 99 | 'TIME', 'TIMEDIFF', 'TIMESTAMP', 'TIMESTAMPADD', 'TIMESTAMPDIFF', 'TIME_FORMAT', 'TIME_TO_SEC', 'TOUCHES', 'TO_DAYS', 'TRIM', 'TRUNCATE', 'UCASE', 100 | 'UNCOMPRESS', 'UNCOMPRESSED_LENGTH', 'UNHEX', 'UNIQUE_USERS', 'UNIX_TIMESTAMP', 'UPDATEXML', 'UPPER', 'USER', 'UTC_DATE', 'UTC_TIME', 'UTC_TIMESTAMP', 101 | 'UUID', 'VARIANCE', 'VAR_POP', 'VAR_SAMP', 'VERSION', 'WEEK', 'WEEKDAY', 'WEEKOFYEAR', 'WITHIN', 'X', 'Y', 'YEAR', 'YEARWEEK' 102 | ); 103 | 104 | // Punctuation that can be used as a boundary between other tokens 105 | protected static $boundaries = array(',', ';',':', ')', '(', '.', '=', '<', '>', '+', '-', '*', '/', '!', '^', '%', '|', '&', '#'); 106 | 107 | // For HTML syntax highlighting 108 | // Styles applied to different token types 109 | public static $quote_attributes = 'style="color: blue;"'; 110 | public static $backtick_quote_attributes = 'style="color: purple;"'; 111 | public static $reserved_attributes = 'style="font-weight:bold;"'; 112 | public static $boundary_attributes = ''; 113 | public static $number_attributes = 'style="color: green;"'; 114 | public static $word_attributes = 'style="color: #333;"'; 115 | public static $error_attributes = 'style="background-color: red;"'; 116 | public static $comment_attributes = 'style="color: #aaa;"'; 117 | public static $variable_attributes = 'style="color: orange;"'; 118 | public static $pre_attributes = 'style="color: black; background-color: white;"'; 119 | 120 | // Boolean - whether or not the current environment is the CLI 121 | // This affects the type of syntax highlighting 122 | // If not defined, it will be determined automatically 123 | public static $cli; 124 | 125 | // For CLI syntax highlighting 126 | public static $cli_quote = "\x1b[34;1m"; 127 | public static $cli_backtick_quote = "\x1b[35;1m"; 128 | public static $cli_reserved = "\x1b[37m"; 129 | public static $cli_boundary = ""; 130 | public static $cli_number = "\x1b[32;1m"; 131 | public static $cli_word = ""; 132 | public static $cli_error = "\x1b[31;1;7m"; 133 | public static $cli_comment = "\x1b[30;1m"; 134 | public static $cli_functions = "\x1b[37m"; 135 | public static $cli_variable = "\x1b[36;1m"; 136 | 137 | // The tab character to use when formatting SQL 138 | public static $tab = ' '; 139 | 140 | // This flag tells us if queries need to be enclosed in
     tags
     141 |     public static $use_pre = true;
     142 | 
     143 |     // This flag tells us if SqlFormatted has been initialized
     144 |     protected static $init;
     145 | 
     146 |     // Regular expressions for tokenizing
     147 |     protected static $regex_boundaries;
     148 |     protected static $regex_reserved;
     149 |     protected static $regex_reserved_newline;
     150 |     protected static $regex_reserved_toplevel;
     151 |     protected static $regex_function;
     152 | 
     153 |     // Cache variables
     154 |     // Only tokens shorter than this size will be cached.  Somewhere between 10 and 20 seems to work well for most cases.
     155 |     public static $max_cachekey_size = 15;
     156 |     protected static $token_cache = array();
     157 |     protected static $cache_hits = 0;
     158 |     protected static $cache_misses = 0;
     159 | 
     160 |     /**
     161 |      * Get stats about the token cache
     162 |      * @return Array An array containing the keys 'hits', 'misses', 'entries', and 'size' in bytes
     163 |      */
     164 |     public static function getCacheStats()
     165 |     {
     166 |         return array(
     167 |             'hits'=>self::$cache_hits,
     168 |             'misses'=>self::$cache_misses,
     169 |             'entries'=>count(self::$token_cache),
     170 |             'size'=>strlen(serialize(self::$token_cache))
     171 |         );
     172 |     }
     173 | 
     174 |     /**
     175 |      * Stuff that only needs to be done once.  Builds regular expressions and sorts the reserved words.
     176 |      */
     177 |     protected static function init()
     178 |     {
     179 |         if (self::$init) return;
     180 | 
     181 |         // Sort reserved word list from longest word to shortest, 3x faster than usort
     182 |         $reservedMap = array_combine(self::$reserved, array_map('strlen', self::$reserved));
     183 |         arsort($reservedMap);
     184 |         self::$reserved = array_keys($reservedMap);
     185 | 
     186 |         // Set up regular expressions
     187 |         self::$regex_boundaries = '('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$boundaries)).')';
     188 |         self::$regex_reserved = '('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$reserved)).')';
     189 |         self::$regex_reserved_toplevel = str_replace(' ','\\s+','('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$reserved_toplevel)).')');
     190 |         self::$regex_reserved_newline = str_replace(' ','\\s+','('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$reserved_newline)).')');
     191 | 
     192 |         self::$regex_function = '('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$functions)).')';
     193 | 
     194 |         self::$init = true;
     195 |     }
     196 | 
     197 |     /**
     198 |      * Return the next token and token type in a SQL string.
     199 |      * Quoted strings, comments, reserved words, whitespace, and punctuation are all their own tokens.
     200 |      *
     201 |      * @param String $string   The SQL string
     202 |      * @param array  $previous The result of the previous getNextToken() call
     203 |      *
     204 |      * @return Array An associative array containing the type and value of the token.
     205 |      */
     206 |     protected static function getNextToken($string, $previous = null)
     207 |     {
     208 |         // Whitespace
     209 |         if (preg_match('/^\s+/',$string,$matches)) {
     210 |             return array(
     211 |                 self::TOKEN_VALUE => $matches[0],
     212 |                 self::TOKEN_TYPE=>self::TOKEN_TYPE_WHITESPACE
     213 |             );
     214 |         }
     215 | 
     216 |         // Comment
     217 |         if ($string[0] === '#' || (isset($string[1])&&($string[0]==='-'&&$string[1]==='-') || ($string[0]==='/'&&$string[1]==='*'))) {
     218 |             // Comment until end of line
     219 |             if ($string[0] === '-' || $string[0] === '#') {
     220 |                 $last = strpos($string, "\n");
     221 |                 $type = self::TOKEN_TYPE_COMMENT;
     222 |             } else { // Comment until closing comment tag
     223 |                 $last = strpos($string, "*/", 2) + 2;
     224 |                 $type = self::TOKEN_TYPE_BLOCK_COMMENT;
     225 |             }
     226 | 
     227 |             if ($last === false) {
     228 |                 $last = strlen($string);
     229 |             }
     230 | 
     231 |             return array(
     232 |                 self::TOKEN_VALUE => substr($string, 0, $last),
     233 |                 self::TOKEN_TYPE  => $type
     234 |             );
     235 |         }
     236 | 
     237 |         // Quoted String
     238 |         if ($string[0]==='"' || $string[0]==='\'' || $string[0]==='`' || $string[0]==='[') {
     239 |             $return = array(
     240 |                 self::TOKEN_TYPE => (($string[0]==='`' || $string[0]==='[')? self::TOKEN_TYPE_BACKTICK_QUOTE : self::TOKEN_TYPE_QUOTE),
     241 |                 self::TOKEN_VALUE => self::getQuotedString($string)
     242 |             );
     243 | 
     244 |             return $return;
     245 |         }
     246 | 
     247 |         // User-defined Variable
     248 |         if (($string[0] === '@' || $string[0] === ':') && isset($string[1])) {
     249 |             $ret = array(
     250 |                 self::TOKEN_VALUE => null,
     251 |                 self::TOKEN_TYPE => self::TOKEN_TYPE_VARIABLE
     252 |             );
     253 |             
     254 |             // If the variable name is quoted
     255 |             if ($string[1]==='"' || $string[1]==='\'' || $string[1]==='`') {
     256 |                 $ret[self::TOKEN_VALUE] = $string[0].self::getQuotedString(substr($string,1));
     257 |             }
     258 |             // Non-quoted variable name
     259 |             else {
     260 |                 preg_match('/^('.$string[0].'[a-zA-Z0-9\._\$]+)/',$string,$matches);
     261 |                 if ($matches) {
     262 |                     $ret[self::TOKEN_VALUE] = $matches[1];
     263 |                 }
     264 |             }
     265 |             
     266 |             if($ret[self::TOKEN_VALUE] !== null) return $ret;
     267 |         }
     268 | 
     269 |         // Number (decimal, binary, or hex)
     270 |         if (preg_match('/^([0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)($|\s|"\'`|'.self::$regex_boundaries.')/',$string,$matches)) {
     271 |             return array(
     272 |                 self::TOKEN_VALUE => $matches[1],
     273 |                 self::TOKEN_TYPE=>self::TOKEN_TYPE_NUMBER
     274 |             );
     275 |         }
     276 | 
     277 |         // Boundary Character (punctuation and symbols)
     278 |         if (preg_match('/^('.self::$regex_boundaries.')/',$string,$matches)) {
     279 |             return array(
     280 |                 self::TOKEN_VALUE => $matches[1],
     281 |                 self::TOKEN_TYPE  => self::TOKEN_TYPE_BOUNDARY
     282 |             );
     283 |         }
     284 | 
     285 |         // A reserved word cannot be preceded by a '.'
     286 |         // this makes it so in "mytable.from", "from" is not considered a reserved word
     287 |         if (!$previous || !isset($previous[self::TOKEN_VALUE]) || $previous[self::TOKEN_VALUE] !== '.') {
     288 |             $upper = strtoupper($string);
     289 |             // Top Level Reserved Word
     290 |             if (preg_match('/^('.self::$regex_reserved_toplevel.')($|\s|'.self::$regex_boundaries.')/', $upper,$matches)) {
     291 |                 return array(
     292 |                     self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED_TOPLEVEL,
     293 |                     self::TOKEN_VALUE=>substr($string,0,strlen($matches[1]))
     294 |                 );
     295 |             }
     296 |             // Newline Reserved Word
     297 |             if (preg_match('/^('.self::$regex_reserved_newline.')($|\s|'.self::$regex_boundaries.')/', $upper,$matches)) {
     298 |                 return array(
     299 |                     self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED_NEWLINE,
     300 |                     self::TOKEN_VALUE=>substr($string,0,strlen($matches[1]))
     301 |                 );
     302 |             }
     303 |             // Other Reserved Word
     304 |             if (preg_match('/^('.self::$regex_reserved.')($|\s|'.self::$regex_boundaries.')/', $upper,$matches)) {
     305 |                 return array(
     306 |                     self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED,
     307 |                     self::TOKEN_VALUE=>substr($string,0,strlen($matches[1]))
     308 |                 );
     309 |             }
     310 |         }
     311 | 
     312 |         // A function must be suceeded by '('
     313 |         // this makes it so "count(" is considered a function, but "count" alone is not
     314 |         $upper = strtoupper($string);
     315 |         // function
     316 |         if (preg_match('/^('.self::$regex_function.'[(]|\s|[)])/', $upper,$matches)) {
     317 |             return array(
     318 |                 self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED,
     319 |                 self::TOKEN_VALUE=>substr($string,0,strlen($matches[1])-1)
     320 |             );
     321 |         }
     322 | 
     323 |         // Non reserved word
     324 |         preg_match('/^(.*?)($|\s|["\'`]|'.self::$regex_boundaries.')/',$string,$matches);
     325 | 
     326 |         return array(
     327 |             self::TOKEN_VALUE => $matches[1],
     328 |             self::TOKEN_TYPE  => self::TOKEN_TYPE_WORD
     329 |         );
     330 |     }
     331 | 
     332 |     protected static function getQuotedString($string)
     333 |     {
     334 |         $ret = null;
     335 |         
     336 |         // This checks for the following patterns:
     337 |         // 1. backtick quoted string using `` to escape
     338 |         // 2. square bracket quoted string (SQL Server) using ]] to escape
     339 |         // 3. double quoted string using "" or \" to escape
     340 |         // 4. single quoted string using '' or \' to escape
     341 |         if ( preg_match('/^(((`[^`]*($|`))+)|((\[[^\]]*($|\]))(\][^\]]*($|\]))*)|(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)|((\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*(\'|$))+))/s', $string, $matches)) {
     342 |             $ret = $matches[1];
     343 |         }
     344 |         
     345 |         return $ret;
     346 |     }
     347 | 
     348 |     /**
     349 |      * Takes a SQL string and breaks it into tokens.
     350 |      * Each token is an associative array with type and value.
     351 |      *
     352 |      * @param String $string The SQL string
     353 |      *
     354 |      * @return Array An array of tokens.
     355 |      */
     356 |     protected static function tokenize($string)
     357 |     {
     358 |         self::init();
     359 | 
     360 |         $tokens = array();
     361 | 
     362 |         // Used for debugging if there is an error while tokenizing the string
     363 |         $original_length = strlen($string);
     364 | 
     365 |         // Used to make sure the string keeps shrinking on each iteration
     366 |         $old_string_len = strlen($string) + 1;
     367 | 
     368 |         $token = null;
     369 | 
     370 |         $current_length = strlen($string);
     371 | 
     372 |         // Keep processing the string until it is empty
     373 |         while ($current_length) {
     374 |             // If the string stopped shrinking, there was a problem
     375 |             if ($old_string_len <= $current_length) {
     376 |                 $tokens[] = array(
     377 |                     self::TOKEN_VALUE=>$string,
     378 |                     self::TOKEN_TYPE=>self::TOKEN_TYPE_ERROR
     379 |                 );
     380 | 
     381 |                 return $tokens;
     382 |             }
     383 |             $old_string_len =  $current_length;
     384 | 
     385 |             // Determine if we can use caching
     386 |             if ($current_length >= self::$max_cachekey_size) {
     387 |                 $cacheKey = substr($string,0,self::$max_cachekey_size);
     388 |             } else {
     389 |                 $cacheKey = false;
     390 |             }
     391 | 
     392 |             // See if the token is already cached
     393 |             if ($cacheKey && isset(self::$token_cache[$cacheKey])) {
     394 |                 // Retrieve from cache
     395 |                 $token = self::$token_cache[$cacheKey];
     396 |                 $token_length = strlen($token[self::TOKEN_VALUE]);
     397 |                 self::$cache_hits++;
     398 |             } else {
     399 |                 // Get the next token and the token type
     400 |                 $token = self::getNextToken($string, $token);
     401 |                 $token_length = strlen($token[self::TOKEN_VALUE]);
     402 |                 self::$cache_misses++;
     403 | 
     404 |                 // If the token is shorter than the max length, store it in cache
     405 |                 if ($cacheKey && $token_length < self::$max_cachekey_size) {
     406 |                     self::$token_cache[$cacheKey] = $token;
     407 |                 }
     408 |             }
     409 | 
     410 |             $tokens[] = $token;
     411 | 
     412 |             // Advance the string
     413 |             $string = substr($string, $token_length);
     414 | 
     415 |             $current_length -= $token_length;
     416 |         }
     417 | 
     418 |         return $tokens;
     419 |     }
     420 | 
     421 |     /**
     422 |      * Format the whitespace in a SQL string to make it easier to read.
     423 |      *
     424 |      * @param String  $string    The SQL string
     425 |      * @param boolean $highlight If true, syntax highlighting will also be performed
     426 |      *
     427 |      * @return String The SQL string with HTML styles and formatting wrapped in a 
     tag
     428 |      */
     429 |     public static function format($string, $highlight=true)
     430 |     {
     431 |         // This variable will be populated with formatted html
     432 |         $return = '';
     433 | 
     434 |         // Use an actual tab while formatting and then switch out with self::$tab at the end
     435 |         $tab = "\t";
     436 | 
     437 |         $indent_level = 0;
     438 |         $newline = false;
     439 |         $inline_parentheses = false;
     440 |         $increase_special_indent = false;
     441 |         $increase_block_indent = false;
     442 |         $indent_types = array();
     443 |         $added_newline = false;
     444 |         $inline_count = 0;
     445 |         $inline_indented = false;
     446 |         $clause_limit = false;
     447 | 
     448 |         // Tokenize String
     449 |         $original_tokens = self::tokenize($string);
     450 | 
     451 |         // Remove existing whitespace
     452 |         $tokens = array();
     453 |         foreach ($original_tokens as $i=>$token) {
     454 |             if ($token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
     455 |                 $token['i'] = $i;
     456 |                 $tokens[] = $token;
     457 |             }
     458 |         }
     459 | 
     460 |         // Format token by token
     461 |         foreach ($tokens as $i=>$token) {
     462 |             // Get highlighted token if doing syntax highlighting
     463 |             if ($highlight) {
     464 |                 $highlighted = self::highlightToken($token);
     465 |             } else { // If returning raw text
     466 |                 $highlighted = $token[self::TOKEN_VALUE];
     467 |             }
     468 | 
     469 |             // If we are increasing the special indent level now
     470 |             if ($increase_special_indent) {
     471 |                 $indent_level++;
     472 |                 $increase_special_indent = false;
     473 |                 array_unshift($indent_types,'special');
     474 |             }
     475 |             // If we are increasing the block indent level now
     476 |             if ($increase_block_indent) {
     477 |                 $indent_level++;
     478 |                 $increase_block_indent = false;
     479 |                 array_unshift($indent_types,'block');
     480 |             }
     481 | 
     482 |             // If we need a new line before the token
     483 |             if ($newline) {
     484 |                 $return .= "\n" . str_repeat($tab, $indent_level);
     485 |                 $newline = false;
     486 |                 $added_newline = true;
     487 |             } else {
     488 |                 $added_newline = false;
     489 |             }
     490 | 
     491 |             // Display comments directly where they appear in the source
     492 |             if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
     493 |                 if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
     494 |                     $indent = str_repeat($tab,$indent_level);
     495 |                     $return .= "\n" . $indent;
     496 |                     $highlighted = str_replace("\n","\n".$indent,$highlighted);
     497 |                 }
     498 | 
     499 |                 $return .= $highlighted;
     500 |                 $newline = true;
     501 |                 continue;
     502 |             }
     503 | 
     504 |             if ($inline_parentheses) {
     505 |                 // End of inline parentheses
     506 |                 if ($token[self::TOKEN_VALUE] === ')') {
     507 |                     $return = rtrim($return,' ');
     508 | 
     509 |                     if ($inline_indented) {
     510 |                         array_shift($indent_types);
     511 |                         $indent_level --;
     512 |                         $return .= "\n" . str_repeat($tab, $indent_level);
     513 |                     }
     514 | 
     515 |                     $inline_parentheses = false;
     516 | 
     517 |                     $return .= $highlighted . ' ';
     518 |                     continue;
     519 |                 }
     520 | 
     521 |                 if ($token[self::TOKEN_VALUE] === ',') {
     522 |                     if ($inline_count >= 30) {
     523 |                         $inline_count = 0;
     524 |                         $newline = true;
     525 |                     }
     526 |                 }
     527 | 
     528 |                 $inline_count += strlen($token[self::TOKEN_VALUE]);
     529 |             }
     530 | 
     531 |             // Opening parentheses increase the block indent level and start a new line
     532 |             if ($token[self::TOKEN_VALUE] === '(') {
     533 |                 // First check if this should be an inline parentheses block
     534 |                 // Examples are "NOW()", "COUNT(*)", "int(10)", key(`somecolumn`), DECIMAL(7,2)
     535 |                 // Allow up to 3 non-whitespace tokens inside inline parentheses
     536 |                 $length = 0;
     537 |                 for ($j=1;$j<=250;$j++) {
     538 |                     // Reached end of string
     539 |                     if (!isset($tokens[$i+$j])) break;
     540 | 
     541 |                     $next = $tokens[$i+$j];
     542 | 
     543 |                     // Reached closing parentheses, able to inline it
     544 |                     if ($next[self::TOKEN_VALUE] === ')') {
     545 |                         $inline_parentheses = true;
     546 |                         $inline_count = 0;
     547 |                         $inline_indented = false;
     548 |                         break;
     549 |                     }
     550 | 
     551 |                     // Reached an invalid token for inline parentheses
     552 |                     if ($next[self::TOKEN_VALUE]===';' || $next[self::TOKEN_VALUE]==='(') {
     553 |                         break;
     554 |                     }
     555 | 
     556 |                     // Reached an invalid token type for inline parentheses
     557 |                     if ($next[self::TOKEN_TYPE]===self::TOKEN_TYPE_RESERVED_TOPLEVEL || $next[self::TOKEN_TYPE]===self::TOKEN_TYPE_RESERVED_NEWLINE || $next[self::TOKEN_TYPE]===self::TOKEN_TYPE_COMMENT || $next[self::TOKEN_TYPE]===self::TOKEN_TYPE_BLOCK_COMMENT) {
     558 |                         break;
     559 |                     }
     560 | 
     561 |                     $length += strlen($next[self::TOKEN_VALUE]);
     562 |                 }
     563 | 
     564 |                 if ($inline_parentheses && $length > 30) {
     565 |                     $increase_block_indent = true;
     566 |                     $inline_indented = true;
     567 |                     $newline = true;
     568 |                 }
     569 | 
     570 |                 // Take out the preceding space unless there was whitespace there in the original query
     571 |                 if (isset($original_tokens[$token['i']-1]) && $original_tokens[$token['i']-1][self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
     572 |                     $return = rtrim($return,' ');
     573 |                 }
     574 | 
     575 |                 if (!$inline_parentheses) {
     576 |                     $increase_block_indent = true;
     577 |                     // Add a newline after the parentheses
     578 |                     $newline = true;
     579 |                 }
     580 | 
     581 |             }
     582 | 
     583 |             // Closing parentheses decrease the block indent level
     584 |             elseif ($token[self::TOKEN_VALUE] === ')') {
     585 |                 // Remove whitespace before the closing parentheses
     586 |                 $return = rtrim($return,' ');
     587 | 
     588 |                 $indent_level--;
     589 | 
     590 |                 // Reset indent level
     591 |                 while ($j=array_shift($indent_types)) {
     592 |                     if ($j==='special') {
     593 |                         $indent_level--;
     594 |                     } else {
     595 |                         break;
     596 |                     }
     597 |                 }
     598 | 
     599 |                 if ($indent_level < 0) {
     600 |                     // This is an error
     601 |                     $indent_level = 0;
     602 | 
     603 |                     if ($highlight) {
     604 |                         $return .= "\n".self::highlightError($token[self::TOKEN_VALUE]);
     605 |                         continue;
     606 |                     }
     607 |                 }
     608 | 
     609 |                 // Add a newline before the closing parentheses (if not already added)
     610 |                 if (!$added_newline) {
     611 |                     $return .= "\n" . str_repeat($tab, $indent_level);
     612 |                 }
     613 |             }
     614 | 
     615 |             // Top level reserved words start a new line and increase the special indent level
     616 |             elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
     617 |                 $increase_special_indent = true;
     618 | 
     619 |                 // If the last indent type was 'special', decrease the special indent for this round
     620 |                 reset($indent_types);
     621 |                 if (current($indent_types)==='special') {
     622 |                     $indent_level--;
     623 |                     array_shift($indent_types);
     624 |                 }
     625 | 
     626 |                 // Add a newline after the top level reserved word
     627 |                 $newline = true;
     628 |                 // Add a newline before the top level reserved word (if not already added)
     629 |                 if (!$added_newline) {
     630 |                     $return .= "\n" . str_repeat($tab, $indent_level);
     631 |                 }
     632 |                 // If we already added a newline, redo the indentation since it may be different now
     633 |                 else {
     634 |                     $return = rtrim($return,$tab).str_repeat($tab, $indent_level);
     635 |                 }
     636 | 
     637 |                 // If the token may have extra whitespace
     638 |                 if (strpos($token[self::TOKEN_VALUE],' ')!==false || strpos($token[self::TOKEN_VALUE],"\n")!==false || strpos($token[self::TOKEN_VALUE],"\t")!==false) {
     639 |                     $highlighted = preg_replace('/\s+/',' ',$highlighted);
     640 |                 }
     641 |                 //if SQL 'LIMIT' clause, start variable to reset newline
     642 |                 if ($token[self::TOKEN_VALUE] === 'LIMIT' && !$inline_parentheses) {
     643 |                     $clause_limit = true;
     644 |                 }
     645 |             }
     646 | 
     647 |             // Checks if we are out of the limit clause
     648 |             elseif ($clause_limit && $token[self::TOKEN_VALUE] !== "," && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_NUMBER && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
     649 |                 $clause_limit = false;
     650 |             }
     651 | 
     652 |             // Commas start a new line (unless within inline parentheses or SQL 'LIMIT' clause)
     653 |             elseif ($token[self::TOKEN_VALUE] === ',' && !$inline_parentheses) {
     654 |                 //If the previous TOKEN_VALUE is 'LIMIT', resets new line
     655 |                 if ($clause_limit === true) {
     656 |                     $newline = false;
     657 |                     $clause_limit = false;
     658 |                 }
     659 |                 // All other cases of commas
     660 |                 else {
     661 |                     $newline = true;
     662 |                 }
     663 |             }
     664 | 
     665 |             // Newline reserved words start a new line
     666 |             elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE) {
     667 |                 // Add a newline before the reserved word (if not already added)
     668 |                 if (!$added_newline) {
     669 |                     $return .= "\n" . str_repeat($tab, $indent_level);
     670 |                 }
     671 | 
     672 |                 // If the token may have extra whitespace
     673 |                 if (strpos($token[self::TOKEN_VALUE],' ')!==false || strpos($token[self::TOKEN_VALUE],"\n")!==false || strpos($token[self::TOKEN_VALUE],"\t")!==false) {
     674 |                     $highlighted = preg_replace('/\s+/',' ',$highlighted);
     675 |                 }
     676 |             }
     677 | 
     678 |             // Multiple boundary characters in a row should not have spaces between them (not including parentheses)
     679 |             elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BOUNDARY) {
     680 |                 if (isset($tokens[$i-1]) && $tokens[$i-1][self::TOKEN_TYPE] === self::TOKEN_TYPE_BOUNDARY) {
     681 |                     if (isset($original_tokens[$token['i']-1]) && $original_tokens[$token['i']-1][self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
     682 |                         $return = rtrim($return,' ');
     683 |                     }
     684 |                 }
     685 |             }
     686 | 
     687 |             // If the token shouldn't have a space before it
     688 |             if ($token[self::TOKEN_VALUE] === '.' || $token[self::TOKEN_VALUE] === ',' || $token[self::TOKEN_VALUE] === ';') {
     689 |                 $return = rtrim($return, ' ');
     690 |             }
     691 | 
     692 |             $return .= $highlighted.' ';
     693 | 
     694 |             // If the token shouldn't have a space after it
     695 |             if ($token[self::TOKEN_VALUE] === '(' || $token[self::TOKEN_VALUE] === '.') {
     696 |                 $return = rtrim($return,' ');
     697 |             }
     698 |             
     699 |             // If this is the "-" of a negative number, it shouldn't have a space after it
     700 |             if($token[self::TOKEN_VALUE] === '-' && isset($tokens[$i+1]) && $tokens[$i+1][self::TOKEN_TYPE] === self::TOKEN_TYPE_NUMBER && isset($tokens[$i-1])) {
     701 |                 $prev = $tokens[$i-1][self::TOKEN_TYPE];
     702 |                 if($prev !== self::TOKEN_TYPE_QUOTE && $prev !== self::TOKEN_TYPE_BACKTICK_QUOTE && $prev !== self::TOKEN_TYPE_WORD && $prev !== self::TOKEN_TYPE_NUMBER) {
     703 |                     $return = rtrim($return,' ');
     704 |                 }
     705 |             } 
     706 |         }
     707 | 
     708 |         // If there are unmatched parentheses
     709 |         if ($highlight && array_search('block',$indent_types) !== false) {
     710 |             $return .= "\n".self::highlightError("WARNING: unclosed parentheses or section");
     711 |         }
     712 | 
     713 |         // Replace tab characters with the configuration tab character
     714 |         $return = trim(str_replace("\t",self::$tab,$return));
     715 | 
     716 |         if ($highlight) {
     717 |             $return = self::output($return);
     718 |         }
     719 | 
     720 |         return $return;
     721 |     }
     722 | 
     723 |     /**
     724 |      * Add syntax highlighting to a SQL string
     725 |      *
     726 |      * @param String $string The SQL string
     727 |      *
     728 |      * @return String The SQL string with HTML styles applied
     729 |      */
     730 |     public static function highlight($string)
     731 |     {
     732 |         $tokens = self::tokenize($string);
     733 | 
     734 |         $return = '';
     735 | 
     736 |         foreach ($tokens as $token) {
     737 |             $return .= self::highlightToken($token);
     738 |         }
     739 | 
     740 |         return self::output($return);
     741 |     }
     742 | 
     743 |     /**
     744 |      * Split a SQL string into multiple queries.
     745 |      * Uses ";" as a query delimiter.
     746 |      *
     747 |      * @param String $string The SQL string
     748 |      *
     749 |      * @return Array An array of individual query strings without trailing semicolons
     750 |      */
     751 |     public static function splitQuery($string)
     752 |     {
     753 |         $queries = array();
     754 |         $current_query = '';
     755 |         $empty = true;
     756 | 
     757 |         $tokens = self::tokenize($string);
     758 | 
     759 |         foreach ($tokens as $token) {
     760 |             // If this is a query separator
     761 |             if ($token[self::TOKEN_VALUE] === ';') {
     762 |                 if (!$empty) {
     763 |                     $queries[] = $current_query.';';
     764 |                 }
     765 |                 $current_query = '';
     766 |                 $empty = true;
     767 |                 continue;
     768 |             }
     769 | 
     770 |             // If this is a non-empty character
     771 |             if ($token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_COMMENT && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_BLOCK_COMMENT) {
     772 |                 $empty = false;
     773 |             }
     774 | 
     775 |             $current_query .= $token[self::TOKEN_VALUE];
     776 |         }
     777 | 
     778 |         if (!$empty) {
     779 |             $queries[] = trim($current_query);
     780 |         }
     781 | 
     782 |         return $queries;
     783 |     }
     784 | 
     785 |     /**
     786 |      * Remove all comments from a SQL string
     787 |      *
     788 |      * @param String $string The SQL string
     789 |      *
     790 |      * @return String The SQL string without comments
     791 |      */
     792 |     public static function removeComments($string)
     793 |     {
     794 |         $result = '';
     795 | 
     796 |         $tokens = self::tokenize($string);
     797 | 
     798 |         foreach ($tokens as $token) {
     799 |             // Skip comment tokens
     800 |             if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
     801 |                 continue;
     802 |             }
     803 | 
     804 |             $result .= $token[self::TOKEN_VALUE];
     805 |         }
     806 |         $result = self::format( $result,false);
     807 | 
     808 |         return $result;
     809 |     }
     810 | 
     811 |     /**
     812 |      * Compress a query by collapsing white space and removing comments
     813 |      *
     814 |      * @param String $string The SQL string
     815 |      *
     816 |      * @return String The SQL string without comments
     817 |      */
     818 |     public static function compress($string)
     819 |     {
     820 |         $result = '';
     821 | 
     822 |         $tokens = self::tokenize($string);
     823 | 
     824 |         $whitespace = true;
     825 |         foreach ($tokens as $token) {
     826 |             // Skip comment tokens
     827 |             if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
     828 |                 continue;
     829 |             }
     830 |             // Remove extra whitespace in reserved words (e.g "OUTER     JOIN" becomes "OUTER JOIN")
     831 |             elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
     832 |                 $token[self::TOKEN_VALUE] = preg_replace('/\s+/',' ',$token[self::TOKEN_VALUE]);
     833 |             }
     834 | 
     835 |             if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_WHITESPACE) {
     836 |                 // If the last token was whitespace, don't add another one
     837 |                 if ($whitespace) {
     838 |                     continue;
     839 |                 } else {
     840 |                     $whitespace = true;
     841 |                     // Convert all whitespace to a single space
     842 |                     $token[self::TOKEN_VALUE] = ' ';
     843 |                 }
     844 |             } else {
     845 |                 $whitespace = false;
     846 |             }
     847 | 
     848 |             $result .= $token[self::TOKEN_VALUE];
     849 |         }
     850 | 
     851 |         return rtrim($result);
     852 |     }
     853 | 
     854 |     /**
     855 |      * Highlights a token depending on its type.
     856 |      *
     857 |      * @param Array $token An associative array containing type and value.
     858 |      *
     859 |      * @return String HTML code of the highlighted token.
     860 |      */
     861 |     protected static function highlightToken($token)
     862 |     {
     863 |         $type = $token[self::TOKEN_TYPE];
     864 | 
     865 |         if (self::is_cli()) {
     866 |             $token = $token[self::TOKEN_VALUE];
     867 |         } else {
     868 |             if (defined('ENT_IGNORE')) {
     869 |               $token = htmlentities($token[self::TOKEN_VALUE],ENT_COMPAT | ENT_IGNORE ,'UTF-8');
     870 |             } else {
     871 |               $token = htmlentities($token[self::TOKEN_VALUE],ENT_COMPAT,'UTF-8');
     872 |             }
     873 |         }
     874 | 
     875 |         if ($type===self::TOKEN_TYPE_BOUNDARY) {
     876 |             return self::highlightBoundary($token);
     877 |         } elseif ($type===self::TOKEN_TYPE_WORD) {
     878 |             return self::highlightWord($token);
     879 |         } elseif ($type===self::TOKEN_TYPE_BACKTICK_QUOTE) {
     880 |             return self::highlightBacktickQuote($token);
     881 |         } elseif ($type===self::TOKEN_TYPE_QUOTE) {
     882 |             return self::highlightQuote($token);
     883 |         } elseif ($type===self::TOKEN_TYPE_RESERVED) {
     884 |             return self::highlightReservedWord($token);
     885 |         } elseif ($type===self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
     886 |             return self::highlightReservedWord($token);
     887 |         } elseif ($type===self::TOKEN_TYPE_RESERVED_NEWLINE) {
     888 |             return self::highlightReservedWord($token);
     889 |         } elseif ($type===self::TOKEN_TYPE_NUMBER) {
     890 |             return self::highlightNumber($token);
     891 |         } elseif ($type===self::TOKEN_TYPE_VARIABLE) {
     892 |             return self::highlightVariable($token);
     893 |         } elseif ($type===self::TOKEN_TYPE_COMMENT || $type===self::TOKEN_TYPE_BLOCK_COMMENT) {
     894 |             return self::highlightComment($token);
     895 |         }
     896 | 
     897 |         return $token;
     898 |     }
     899 | 
     900 |     /**
     901 |      * Highlights a quoted string
     902 |      *
     903 |      * @param String $value The token's value
     904 |      *
     905 |      * @return String HTML code of the highlighted token.
     906 |      */
     907 |     protected static function highlightQuote($value)
     908 |     {
     909 |         if (self::is_cli()) {
     910 |             return self::$cli_quote . $value . "\x1b[0m";
     911 |         } else {
     912 |             return '' . $value . '';
     913 |         }
     914 |     }
     915 | 
     916 |     /**
     917 |      * Highlights a backtick quoted string
     918 |      *
     919 |      * @param String $value The token's value
     920 |      *
     921 |      * @return String HTML code of the highlighted token.
     922 |      */
     923 |     protected static function highlightBacktickQuote($value)
     924 |     {
     925 |         if (self::is_cli()) {
     926 |             return self::$cli_backtick_quote . $value . "\x1b[0m";
     927 |         } else {
     928 |             return '' . $value . '';
     929 |         }
     930 |     }
     931 | 
     932 |     /**
     933 |      * Highlights a reserved word
     934 |      *
     935 |      * @param String $value The token's value
     936 |      *
     937 |      * @return String HTML code of the highlighted token.
     938 |      */
     939 |     protected static function highlightReservedWord($value)
     940 |     {
     941 |         if (self::is_cli()) {
     942 |             return self::$cli_reserved . $value . "\x1b[0m";
     943 |         } else {
     944 |             return '' . $value . '';
     945 |         }
     946 |     }
     947 | 
     948 |     /**
     949 |      * Highlights a boundary token
     950 |      *
     951 |      * @param String $value The token's value
     952 |      *
     953 |      * @return String HTML code of the highlighted token.
     954 |      */
     955 |     protected static function highlightBoundary($value)
     956 |     {
     957 |         if ($value==='(' || $value===')') return $value;
     958 | 
     959 |         if (self::is_cli()) {
     960 |             return self::$cli_boundary . $value . "\x1b[0m";
     961 |         } else {
     962 |             return '' . $value . '';
     963 |         }
     964 |     }
     965 | 
     966 |     /**
     967 |      * Highlights a number
     968 |      *
     969 |      * @param String $value The token's value
     970 |      *
     971 |      * @return String HTML code of the highlighted token.
     972 |      */
     973 |     protected static function highlightNumber($value)
     974 |     {
     975 |         if (self::is_cli()) {
     976 |             return self::$cli_number . $value . "\x1b[0m";
     977 |         } else {
     978 |             return '' . $value . '';
     979 |         }
     980 |     }
     981 | 
     982 |     /**
     983 |      * Highlights an error
     984 |      *
     985 |      * @param String $value The token's value
     986 |      *
     987 |      * @return String HTML code of the highlighted token.
     988 |      */
     989 |     protected static function highlightError($value)
     990 |     {
     991 |         if (self::is_cli()) {
     992 |             return self::$cli_error . $value . "\x1b[0m";
     993 |         } else {
     994 |             return '' . $value . '';
     995 |         }
     996 |     }
     997 | 
     998 |     /**
     999 |      * Highlights a comment
    1000 |      *
    1001 |      * @param String $value The token's value
    1002 |      *
    1003 |      * @return String HTML code of the highlighted token.
    1004 |      */
    1005 |     protected static function highlightComment($value)
    1006 |     {
    1007 |         if (self::is_cli()) {
    1008 |             return self::$cli_comment . $value . "\x1b[0m";
    1009 |         } else {
    1010 |             return '' . $value . '';
    1011 |         }
    1012 |     }
    1013 | 
    1014 |     /**
    1015 |      * Highlights a word token
    1016 |      *
    1017 |      * @param String $value The token's value
    1018 |      *
    1019 |      * @return String HTML code of the highlighted token.
    1020 |      */
    1021 |     protected static function highlightWord($value)
    1022 |     {
    1023 |         if (self::is_cli()) {
    1024 |             return self::$cli_word . $value . "\x1b[0m";
    1025 |         } else {
    1026 |             return '' . $value . '';
    1027 |         }
    1028 |     }
    1029 | 
    1030 |     /**
    1031 |      * Highlights a variable token
    1032 |      *
    1033 |      * @param String $value The token's value
    1034 |      *
    1035 |      * @return String HTML code of the highlighted token.
    1036 |      */
    1037 |     protected static function highlightVariable($value)
    1038 |     {
    1039 |         if (self::is_cli()) {
    1040 |             return self::$cli_variable . $value . "\x1b[0m";
    1041 |         } else {
    1042 |             return '' . $value . '';
    1043 |         }
    1044 |     }
    1045 | 
    1046 |     /**
    1047 |      * Helper function for building regular expressions for reserved words and boundary characters
    1048 |      *
    1049 |      * @param String $a The string to be quoted
    1050 |      *
    1051 |      * @return String The quoted string
    1052 |      */
    1053 |     private static function quote_regex($a)
    1054 |     {
    1055 |         return preg_quote($a,'/');
    1056 |     }
    1057 | 
    1058 |     /**
    1059 |      * Helper function for building string output
    1060 |      *
    1061 |      * @param String $string The string to be quoted
    1062 |      *
    1063 |      * @return String The quoted string
    1064 |      */
    1065 |     private static function output($string)
    1066 |     {
    1067 |         if (self::is_cli()) {
    1068 |             return $string."\n";
    1069 |         } else {
    1070 |             $string=trim($string);
    1071 |             if (!self::$use_pre) {
    1072 |                 return $string;
    1073 |             }
    1074 | 
    1075 |             return '
    ' . $string . '
    '; 1076 | } 1077 | } 1078 | 1079 | private static function is_cli() 1080 | { 1081 | if (isset(self::$cli)) return self::$cli; 1082 | else return php_sapi_name() === 'cli'; 1083 | } 1084 | 1085 | } 1086 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ./tests 7 | 8 | 9 | 10 | 11 | 12 | ./ 13 | 14 | ./tests 15 | ./vendor 16 | ./examples 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/SqlFormatterTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(trim($html), trim(SqlFormatter::format($sql))); 15 | } 16 | /** 17 | * @dataProvider formatData 18 | */ 19 | function testFormat($sql, $html) { 20 | $this->assertEquals(trim($html), trim(SqlFormatter::format($sql, false))); 21 | } 22 | /** 23 | * @dataProvider highlightData 24 | */ 25 | function testHighlight($sql, $html) { 26 | $this->assertEquals(trim($html), trim(SqlFormatter::highlight($sql))); 27 | } 28 | 29 | function testHighlightBinary() { 30 | $sql = 'SELECT "' . pack('H*', "ed180e98a47a45b3bdd304b798bc5797") . '" AS BINARY'; 31 | 32 | if (defined('ENT_IGNORE')) { 33 | // this is what gets written as string 34 | $binaryData = '"' . pack('H*', "180e7a450457") . '"'; 35 | } else { 36 | $binaryData = ''; 37 | } 38 | 39 | $html = '
    SELECT ' . $binaryData . ' AS BINARY
    '; 40 | 41 | $this->assertEquals(trim($html), trim(SqlFormatter::highlight($sql))); 42 | } 43 | /** 44 | * @dataProvider highlightCliData 45 | */ 46 | function testCliHighlight($sql, $html) { 47 | SqlFormatter::$cli = true; 48 | $this->assertEquals(trim($html), trim(SqlFormatter::format($sql))); 49 | SqlFormatter::$cli = false; 50 | } 51 | /** 52 | * @dataProvider compressData 53 | */ 54 | function testCompress($sql, $html) { 55 | $this->assertEquals(trim($html), trim(SqlFormatter::compress($sql))); 56 | } 57 | 58 | function testUsePre() { 59 | SqlFormatter::$use_pre = false; 60 | $actual = SqlFormatter::highlight("test"); 61 | $expected = 'test'; 62 | $this->assertEquals($actual,$expected); 63 | 64 | SqlFormatter::$use_pre = true; 65 | $actual = SqlFormatter::highlight("test"); 66 | $expected = '
    test
    '; 67 | $this->assertEquals($actual,$expected); 68 | } 69 | 70 | function testSplitQuery() { 71 | $expected = array( 72 | "SELECT 'test' FROM MyTable;", 73 | "SELECT Column2 FROM SomeOther Table WHERE (test = true);" 74 | ); 75 | 76 | $actual = SqlFormatter::splitQuery(implode(';',$expected)); 77 | 78 | $this->assertEquals($expected, $actual); 79 | } 80 | 81 | function testSplitQueryEmpty() { 82 | $sql = "SELECT 1;SELECT 2;\n-- This is a comment\n;SELECT 3"; 83 | $expected = array("SELECT 1;","SELECT 2;","SELECT 3"); 84 | $actual = SqlFormatter::splitQuery($sql); 85 | 86 | $this->assertEquals($expected, $actual); 87 | } 88 | 89 | function testRemoveComments() { 90 | $expected = SqlFormatter::format("SELECT\n * FROM\n MyTable",false); 91 | $sql = "/* this is a comment */SELECT#This is another comment\n * FROM-- One final comment\n MyTable"; 92 | $actual = SqlFormatter::removeComments($sql); 93 | 94 | $this->assertEquals($expected, $actual); 95 | } 96 | 97 | function testCacheStats() { 98 | $stats = SqlFormatter::getCacheStats(); 99 | $this->assertGreaterThan(1,$stats['hits']); 100 | } 101 | 102 | function formatHighlightData() { 103 | $formatHighlightData = explode("\n\n",file_get_contents(__DIR__."/format-highlight.html")); 104 | $sqlData = $this->sqlData(); 105 | 106 | $return = array(); 107 | foreach($formatHighlightData as $i=>$data) { 108 | $return[] = array( 109 | $sqlData[$i], 110 | $data 111 | ); 112 | } 113 | 114 | return $return; 115 | } 116 | 117 | function highlightCliData() { 118 | $clidata = explode("\n\n",file_get_contents(__DIR__."/clihighlight.html")); 119 | $sqlData = $this->sqlData(); 120 | 121 | $return = array(); 122 | foreach($clidata as $i=>$data) { 123 | $return[] = array( 124 | $sqlData[$i], 125 | $data 126 | ); 127 | } 128 | 129 | return $return; 130 | } 131 | 132 | function formatData() { 133 | $formatData = explode("\n\n",file_get_contents(__DIR__."/format.html")); 134 | $sqlData = $this->sqlData(); 135 | 136 | $return = array(); 137 | foreach($formatData as $i=>$data) { 138 | $return[] = array( 139 | $sqlData[$i], 140 | $data 141 | ); 142 | } 143 | 144 | return $return; 145 | } 146 | 147 | function compressData() { 148 | $compressData = explode("\n\n",file_get_contents(__DIR__."/compress.html")); 149 | $sqlData = $this->sqlData(); 150 | 151 | $return = array(); 152 | foreach($compressData as $i=>$data) { 153 | $return[] = array( 154 | $sqlData[$i], 155 | $data 156 | ); 157 | } 158 | 159 | return $return; 160 | } 161 | 162 | function highlightData() { 163 | $highlightData = explode("\n\n",file_get_contents(__DIR__."/highlight.html")); 164 | $sqlData = $this->sqlData(); 165 | 166 | $return = array(); 167 | foreach($highlightData as $i=>$data) { 168 | $return[] = array( 169 | $sqlData[$i], 170 | $data 171 | ); 172 | } 173 | 174 | return $return; 175 | } 176 | 177 | 178 | 179 | function sqlData() { 180 | if(!$this->sqlData) { 181 | $this->sqlData = explode("\n\n",file_get_contents(__DIR__."/sql.sql")); 182 | } 183 | 184 | /** 185 | $formatHighlight = array(); 186 | $highlight = array(); 187 | $format = array(); 188 | $compress = array(); 189 | $clihighlight = array(); 190 | 191 | foreach($this->sqlData as $sql) { 192 | $formatHighlight[] = trim(SqlFormatter::format($sql)); 193 | $highlight[] = trim(SqlFormatter::highlight($sql)); 194 | $format[] = trim(SqlFormatter::format($sql, false)); 195 | $compress[] = trim(SqlFormatter::compress($sql)); 196 | 197 | SqlFormatter::$cli = true; 198 | $clihighlight[] = trim(SqlFormatter::format($sql)); 199 | SqlFormatter::$cli = false; 200 | } 201 | 202 | file_put_contents(__DIR__."/format-highlight.html", implode("\n\n",$formatHighlight)); 203 | file_put_contents(__DIR__."/highlight.html", implode("\n\n",$highlight)); 204 | file_put_contents(__DIR__."/format.html", implode("\n\n",$format)); 205 | file_put_contents(__DIR__."/compress.html", implode("\n\n",$compress)); 206 | file_put_contents(__DIR__."/clihighlight.html", implode("\n\n",$clihighlight)); 207 | /**/ 208 | 209 | return $this->sqlData; 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /tests/clihighlight.html: -------------------------------------------------------------------------------- 1 | SELECT 2 | customer_id, 3 | customer_name, 4 | COUNT(order_id) as total 5 | FROM 6 | customers 7 | INNER JOIN orders ON customers.customer_id = orders.customer_id 8 | GROUP BY 9 | customer_id, 10 | customer_name 11 | HAVING 12 | COUNT(order_id) > 5 13 | ORDER BY 14 | COUNT(order_id) DESC; 15 | 16 | UPDATE 17 | customers 18 | SET 19 | totalorders = ordersummary.total 20 | FROM 21 | ( 22 | SELECT 23 | customer_id, 24 | count(order_id) As total 25 | FROM 26 | orders 27 | GROUP BY 28 | customer_id 29 | ) As ordersummary 30 | WHERE 31 | customers.customer_id = ordersummary.customer_id 32 | 33 | SELECT 34 | * 35 | FROM 36 | sometable 37 | UNION ALL 38 | SELECT 39 | * 40 | FROM 41 | someothertable; 42 | 43 | SET 44 | NAMES 'utf8'; 45 | 46 | CREATE TABLE `PREFIX_address` ( 47 | `id_address` int(10) unsigned NOT NULL auto_increment, 48 | `id_country` int(10) unsigned NOT NULL, 49 | `id_state` int(10) unsigned default NULL, 50 | `id_customer` int(10) unsigned NOT NULL default '0', 51 | `id_manufacturer` int(10) unsigned NOT NULL default '0', 52 | `id_supplier` int(10) unsigned NOT NULL default '0', 53 | `id_warehouse` int(10) unsigned NOT NULL default '0', 54 | `alias` varchar(32) NOT NULL, 55 | `company` varchar(64) default NULL, 56 | `lastname` varchar(32) NOT NULL, 57 | `firstname` varchar(32) NOT NULL, 58 | `address1` varchar(128) NOT NULL, 59 | `address2` varchar(128) default NULL, 60 | `postcode` varchar(12) default NULL, 61 | `city` varchar(64) NOT NULL, 62 | `other` text, 63 | `phone` varchar(16) default NULL, 64 | `phone_mobile` varchar(16) default NULL, 65 | `vat_number` varchar(32) default NULL, 66 | `dni` varchar(16) DEFAULT NULL, 67 | `date_add` datetime NOT NULL, 68 | `date_upd` datetime NOT NULL, 69 | `active` tinyint(1) unsigned NOT NULL default '1', 70 | `deleted` tinyint(1) unsigned NOT NULL default '0', 71 | PRIMARY KEY (`id_address`), 72 | KEY `address_customer` (`id_customer`), 73 | KEY `id_country` (`id_country`), 74 | KEY `id_state` (`id_state`), 75 | KEY `id_manufacturer` (`id_manufacturer`), 76 | KEY `id_supplier` (`id_supplier`), 77 | KEY `id_warehouse` (`id_warehouse`) 78 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 79 | 80 | CREATE TABLE `PREFIX_alias` ( 81 | `id_alias` int(10) unsigned NOT NULL auto_increment, 82 | `alias` varchar(255) NOT NULL, 83 | `search` varchar(255) NOT NULL, 84 | `active` tinyint(1) NOT NULL default '1', 85 | PRIMARY KEY (`id_alias`), 86 | UNIQUE KEY `alias` (`alias`) 87 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 88 | 89 | CREATE TABLE `PREFIX_carrier` ( 90 | `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, 91 | `id_reference` int(10) unsigned NOT NULL, 92 | `id_tax_rules_group` int(10) unsigned DEFAULT '0', 93 | `name` varchar(64) NOT NULL, 94 | `url` varchar(255) DEFAULT NULL, 95 | `active` tinyint(1) unsigned NOT NULL DEFAULT '0', 96 | `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', 97 | `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', 98 | `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', 99 | `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', 100 | `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', 101 | `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', 102 | `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', 103 | `external_module_name` varchar(64) DEFAULT NULL, 104 | `shipping_method` int(2) NOT NULL DEFAULT '0', 105 | `position` int(10) unsigned NOT NULL default '0', 106 | `max_width` int(10) DEFAULT 0, 107 | `max_height` int(10) DEFAULT 0, 108 | `max_depth` int(10) DEFAULT 0, 109 | `max_weight` int(10) DEFAULT 0, 110 | `grade` int(10) DEFAULT 0, 111 | PRIMARY KEY (`id_carrier`), 112 | KEY `deleted` (`deleted`, `active`), 113 | KEY `id_tax_rules_group` (`id_tax_rules_group`) 114 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 115 | 116 | CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( 117 | `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, 118 | `name` VARCHAR(255) NOT NULL, 119 | `id_shop` int(11) unsigned NOT NULL DEFAULT '1', 120 | `id_currency` int(10) unsigned NOT NULL, 121 | `id_country` int(10) unsigned NOT NULL, 122 | `id_group` int(10) unsigned NOT NULL, 123 | `from_quantity` mediumint(8) unsigned NOT NULL, 124 | `price` DECIMAL(20, 6), 125 | `reduction` decimal(20, 6) NOT NULL, 126 | `reduction_type` enum('amount', 'percentage') NOT NULL, 127 | `from` datetime NOT NULL, 128 | `to` datetime NOT NULL, 129 | PRIMARY KEY (`id_specific_price_rule`), 130 | KEY `id_product` ( 131 | `id_shop`, `id_currency`, `id_country`, 132 | `id_group`, `from_quantity`, `from`, 133 | `to` 134 | ) 135 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 136 | 137 | UPDATE 138 | `PREFIX_configuration` 139 | SET 140 | value = '6' 141 | WHERE 142 | name = 'PS_SEARCH_WEIGHT_PNAME' 143 | 144 | UPDATE 145 | `PREFIX_hook_module` 146 | SET 147 | position = 1 148 | WHERE 149 | id_hook = ( 150 | SELECT 151 | id_hook 152 | FROM 153 | `PREFIX_hook` 154 | WHERE 155 | name = 'displayPayment' 156 | ) 157 | AND id_module = ( 158 | SELECT 159 | id_module 160 | FROM 161 | `PREFIX_module` 162 | WHERE 163 | name = 'cheque' 164 | ) 165 | OR id_hook = ( 166 | SELECT 167 | id_hook 168 | FROM 169 | `PREFIX_hook` 170 | WHERE 171 | name = 'displayPaymentReturn' 172 | ) 173 | AND id_module = ( 174 | SELECT 175 | id_module 176 | FROM 177 | `PREFIX_module` 178 | WHERE 179 | name = 'cheque' 180 | ) 181 | OR id_hook = ( 182 | SELECT 183 | id_hook 184 | FROM 185 | `PREFIX_hook` 186 | WHERE 187 | name = 'displayHome' 188 | ) 189 | AND id_module = ( 190 | SELECT 191 | id_module 192 | FROM 193 | `PREFIX_module` 194 | WHERE 195 | name = 'homeslider' 196 | ) 197 | OR id_hook = ( 198 | SELECT 199 | id_hook 200 | FROM 201 | `PREFIX_hook` 202 | WHERE 203 | name = 'actionAuthentication' 204 | ) 205 | AND id_module = ( 206 | SELECT 207 | id_module 208 | FROM 209 | `PREFIX_module` 210 | WHERE 211 | name = 'statsdata' 212 | ) 213 | OR id_hook = ( 214 | SELECT 215 | id_hook 216 | FROM 217 | `PREFIX_hook` 218 | WHERE 219 | name = 'actionShopDataDuplication' 220 | ) 221 | AND id_module = ( 222 | SELECT 223 | id_module 224 | FROM 225 | `PREFIX_module` 226 | WHERE 227 | name = 'homeslider' 228 | ) 229 | OR id_hook = ( 230 | SELECT 231 | id_hook 232 | FROM 233 | `PREFIX_hook` 234 | WHERE 235 | name = 'displayTop' 236 | ) 237 | AND id_module = ( 238 | SELECT 239 | id_module 240 | FROM 241 | `PREFIX_module` 242 | WHERE 243 | name = 'blocklanguages' 244 | ) 245 | OR id_hook = ( 246 | SELECT 247 | id_hook 248 | FROM 249 | `PREFIX_hook` 250 | WHERE 251 | name = 'actionCustomerAccountAdd' 252 | ) 253 | AND id_module = ( 254 | SELECT 255 | id_module 256 | FROM 257 | `PREFIX_module` 258 | WHERE 259 | name = 'statsdata' 260 | ) 261 | OR id_hook = ( 262 | SELECT 263 | id_hook 264 | FROM 265 | `PREFIX_hook` 266 | WHERE 267 | name = 'displayCustomerAccount' 268 | ) 269 | AND id_module = ( 270 | SELECT 271 | id_module 272 | FROM 273 | `PREFIX_module` 274 | WHERE 275 | name = 'favoriteproducts' 276 | ) 277 | OR id_hook = ( 278 | SELECT 279 | id_hook 280 | FROM 281 | `PREFIX_hook` 282 | WHERE 283 | name = 'displayAdminStatsModules' 284 | ) 285 | AND id_module = ( 286 | SELECT 287 | id_module 288 | FROM 289 | `PREFIX_module` 290 | WHERE 291 | name = 'statsvisits' 292 | ) 293 | OR id_hook = ( 294 | SELECT 295 | id_hook 296 | FROM 297 | `PREFIX_hook` 298 | WHERE 299 | name = 'displayAdminStatsGraphEngine' 300 | ) 301 | AND id_module = ( 302 | SELECT 303 | id_module 304 | FROM 305 | `PREFIX_module` 306 | WHERE 307 | name = 'graphvisifire' 308 | ) 309 | OR id_hook = ( 310 | SELECT 311 | id_hook 312 | FROM 313 | `PREFIX_hook` 314 | WHERE 315 | name = 'displayAdminStatsGridEngine' 316 | ) 317 | AND id_module = ( 318 | SELECT 319 | id_module 320 | FROM 321 | `PREFIX_module` 322 | WHERE 323 | name = 'gridhtml' 324 | ) 325 | OR id_hook = ( 326 | SELECT 327 | id_hook 328 | FROM 329 | `PREFIX_hook` 330 | WHERE 331 | name = 'displayLeftColumnProduct' 332 | ) 333 | AND id_module = ( 334 | SELECT 335 | id_module 336 | FROM 337 | `PREFIX_module` 338 | WHERE 339 | name = 'blocksharefb' 340 | ) 341 | OR id_hook = ( 342 | SELECT 343 | id_hook 344 | FROM 345 | `PREFIX_hook` 346 | WHERE 347 | name = 'actionSearch' 348 | ) 349 | AND id_module = ( 350 | SELECT 351 | id_module 352 | FROM 353 | `PREFIX_module` 354 | WHERE 355 | name = 'statssearch' 356 | ) 357 | OR id_hook = ( 358 | SELECT 359 | id_hook 360 | FROM 361 | `PREFIX_hook` 362 | WHERE 363 | name = 'actionCategoryAdd' 364 | ) 365 | AND id_module = ( 366 | SELECT 367 | id_module 368 | FROM 369 | `PREFIX_module` 370 | WHERE 371 | name = 'blockcategories' 372 | ) 373 | OR id_hook = ( 374 | SELECT 375 | id_hook 376 | FROM 377 | `PREFIX_hook` 378 | WHERE 379 | name = 'actionCategoryUpdate' 380 | ) 381 | AND id_module = ( 382 | SELECT 383 | id_module 384 | FROM 385 | `PREFIX_module` 386 | WHERE 387 | name = 'blockcategories' 388 | ) 389 | OR id_hook = ( 390 | SELECT 391 | id_hook 392 | FROM 393 | `PREFIX_hook` 394 | WHERE 395 | name = 'actionCategoryDelete' 396 | ) 397 | AND id_module = ( 398 | SELECT 399 | id_module 400 | FROM 401 | `PREFIX_module` 402 | WHERE 403 | name = 'blockcategories' 404 | ) 405 | OR id_hook = ( 406 | SELECT 407 | id_hook 408 | FROM 409 | `PREFIX_hook` 410 | WHERE 411 | name = 'actionAdminMetaSave' 412 | ) 413 | AND id_module = ( 414 | SELECT 415 | id_module 416 | FROM 417 | `PREFIX_module` 418 | WHERE 419 | name = 'blockcategories' 420 | ) 421 | OR id_hook = ( 422 | SELECT 423 | id_hook 424 | FROM 425 | `PREFIX_hook` 426 | WHERE 427 | name = 'displayMyAccountBlock' 428 | ) 429 | AND id_module = ( 430 | SELECT 431 | id_module 432 | FROM 433 | `PREFIX_module` 434 | WHERE 435 | name = 'favoriteproducts' 436 | ) 437 | OR id_hook = ( 438 | SELECT 439 | id_hook 440 | FROM 441 | `PREFIX_hook` 442 | WHERE 443 | name = 'displayFooter' 444 | ) 445 | AND id_module = ( 446 | SELECT 447 | id_module 448 | FROM 449 | `PREFIX_module` 450 | WHERE 451 | name = 'blockreinsurance' 452 | ) 453 | 454 | ALTER TABLE 455 | `PREFIX_employee` 456 | ADD 457 | `bo_color` varchar(32) default NULL 458 | AFTER 459 | `stats_date_to` 460 | 461 | INSERT INTO `PREFIX_cms_category_lang` 462 | VALUES 463 | ( 464 | 1, 3, 'Inicio', '', 'home', NULL, NULL, 465 | NULL 466 | ) 467 | 468 | INSERT INTO `PREFIX_cms_category` 469 | VALUES 470 | (1, 0, 0, 1, NOW(), NOW(), 0) 471 | 472 | UPDATE 473 | `PREFIX_cms_category` 474 | SET 475 | `position` = 0 476 | 477 | ALTER TABLE 478 | `PREFIX_customer` 479 | ADD 480 | `note` text 481 | AFTER 482 | `secure_key` 483 | 484 | ALTER TABLE 485 | `PREFIX_contact` 486 | ADD 487 | `customer_service` tinyint(1) NOT NULL DEFAULT 0 488 | AFTER 489 | `email` 490 | 491 | INSERT INTO `PREFIX_specific_price` ( 492 | `id_product`, `id_shop`, `id_currency`, 493 | `id_country`, `id_group`, `priority`, 494 | `price`, `from_quantity`, `reduction`, 495 | `reduction_type`, `from`, `to` 496 | ) ( 497 | SELECT 498 | dq.`id_product`, 499 | 1, 500 | 1, 501 | 0, 502 | 1, 503 | 0, 504 | 0.00, 505 | dq.`quantity`, 506 | IF( 507 | dq.`id_discount_type` = 2, dq.`value`, 508 | dq.`value` / 100 509 | ), 510 | IF ( 511 | dq.`id_discount_type` = 2, 'amount', 512 | 'percentage' 513 | ), 514 | '0000-00-00 00:00:00', 515 | '0000-00-00 00:00:00' 516 | FROM 517 | `PREFIX_discount_quantity` dq 518 | INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) 519 | ) 520 | 521 | DROP 522 | TABLE `PREFIX_discount_quantity` 523 | 524 | INSERT INTO `PREFIX_specific_price` ( 525 | `id_product`, `id_shop`, `id_currency`, 526 | `id_country`, `id_group`, `priority`, 527 | `price`, `from_quantity`, `reduction`, 528 | `reduction_type`, `from`, `to` 529 | ) ( 530 | SELECT 531 | p.`id_product`, 532 | 1, 533 | 0, 534 | 0, 535 | 0, 536 | 0, 537 | 0.00, 538 | 1, 539 | IF( 540 | p.`reduction_price` > 0, p.`reduction_price`, 541 | p.`reduction_percent` / 100 542 | ), 543 | IF( 544 | p.`reduction_price` > 0, 'amount', 545 | 'percentage' 546 | ), 547 | IF ( 548 | p.`reduction_from` = p.`reduction_to`, 549 | '0000-00-00 00:00:00', p.`reduction_from` 550 | ), 551 | IF ( 552 | p.`reduction_from` = p.`reduction_to`, 553 | '0000-00-00 00:00:00', p.`reduction_to` 554 | ) 555 | FROM 556 | `PREFIX_product` p 557 | WHERE 558 | p.`reduction_price` 559 | OR p.`reduction_percent` 560 | ) 561 | 562 | ALTER TABLE 563 | `PREFIX_product` 564 | DROP 565 | `reduction_price`, 566 | DROP 567 | `reduction_percent`, 568 | DROP 569 | `reduction_from`, 570 | DROP 571 | `reduction_to` 572 | 573 | INSERT INTO `PREFIX_configuration` ( 574 | `name`, `value`, `date_add`, `date_upd` 575 | ) 576 | VALUES 577 | ( 578 | 'PS_SPECIFIC_PRICE_PRIORITIES', 579 | 'id_shop;id_currency;id_country;id_group', 580 | NOW(), NOW() 581 | ), 582 | ('PS_TAX_DISPLAY', 0, NOW(), NOW()), 583 | ( 584 | 'PS_SMARTY_FORCE_COMPILE', 1, NOW(), 585 | NOW() 586 | ), 587 | ( 588 | 'PS_DISTANCE_UNIT', 'km', NOW(), NOW() 589 | ), 590 | ( 591 | 'PS_STORES_DISPLAY_CMS', 0, NOW(), 592 | NOW() 593 | ), 594 | ( 595 | 'PS_STORES_DISPLAY_FOOTER', 0, NOW(), 596 | NOW() 597 | ), 598 | ( 599 | 'PS_STORES_SIMPLIFIED', 0, NOW(), 600 | NOW() 601 | ), 602 | ( 603 | 'PS_STATSDATA_CUSTOMER_PAGESVIEWS', 604 | 1, NOW(), NOW() 605 | ), 606 | ( 607 | 'PS_STATSDATA_PAGESVIEWS', 1, NOW(), 608 | NOW() 609 | ), 610 | ( 611 | 'PS_STATSDATA_PLUGINS', 1, NOW(), 612 | NOW() 613 | ) 614 | 615 | INSERT INTO `PREFIX_configuration` ( 616 | `name`, `value`, `date_add`, `date_upd` 617 | ) 618 | VALUES 619 | ( 620 | 'PS_CONDITIONS_CMS_ID', 621 | IFNULL( 622 | ( 623 | SELECT 624 | `id_cms` 625 | FROM 626 | `PREFIX_cms` 627 | WHERE 628 | `id_cms` = 3 629 | ), 630 | 0 631 | ), 632 | NOW(), 633 | NOW() 634 | ) 635 | 636 | CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (`value` text) 637 | 638 | SET 639 | @defaultOOS = ( 640 | SELECT 641 | value 642 | FROM 643 | `PREFIX_configuration` 644 | WHERE 645 | name = 'PS_ORDER_OUT_OF_STOCK' 646 | ) 647 | 648 | UPDATE 649 | `PREFIX_product` p 650 | SET 651 | `cache_default_attribute` = 0 652 | WHERE 653 | `id_product` NOT IN ( 654 | SELECT 655 | `id_product` 656 | FROM 657 | `PREFIX_product_attribute` 658 | ) 659 | 660 | INSERT INTO `PREFIX_hook` ( 661 | `name`, `title`, `description`, `position` 662 | ) 663 | VALUES 664 | ( 665 | 'processCarrier', 'Carrier Process', 666 | NULL, 0 667 | ) 668 | 669 | INSERT INTO `PREFIX_stock_mvt_reason_lang` ( 670 | `id_stock_mvt_reason`, `id_lang`, 671 | `name` 672 | ) 673 | VALUES 674 | (1, 1, 'Order'), 675 | (1, 2, 'Commande'), 676 | (2, 1, 'Missing Stock Movement'), 677 | ( 678 | 2, 2, 'Mouvement de stock manquant' 679 | ), 680 | (3, 1, 'Restocking'), 681 | (3, 2, 'Réassort') 682 | 683 | INSERT INTO `PREFIX_meta_lang` ( 684 | `id_lang`, `id_meta`, `title`, `url_rewrite` 685 | ) 686 | VALUES 687 | ( 688 | 1, 689 | ( 690 | SELECT 691 | `id_meta` 692 | FROM 693 | `PREFIX_meta` 694 | WHERE 695 | `page` = 'authentication' 696 | ), 697 | 'Authentication', 698 | 'authentication' 699 | ), 700 | ( 701 | 2, 702 | ( 703 | SELECT 704 | `id_meta` 705 | FROM 706 | `PREFIX_meta` 707 | WHERE 708 | `page` = 'authentication' 709 | ), 710 | 'Authentification', 711 | 'authentification' 712 | ), 713 | ( 714 | 3, 715 | ( 716 | SELECT 717 | `id_meta` 718 | FROM 719 | `PREFIX_meta` 720 | WHERE 721 | `page` = 'authentication' 722 | ), 723 | 'Autenticación', 724 | 'autenticacion' 725 | ) 726 | 727 | LOCK TABLES `admin_assert` WRITE 728 | 729 | UNLOCK TABLES 730 | 731 | DROP 732 | TABLE IF EXISTS `admin_role` 733 | 734 | SELECT 735 | * 736 | FROM 737 | -- This is another comment 738 | MyTable # One final comment 739 | 740 | /* This is a block comment 741 | */ 742 | WHERE 743 | 1 = 2; 744 | 745 | SELECT 746 | -- This is a test 747 | 748 | SELECT 749 | Test 750 | FROM 751 | Test 752 | WHERE 753 | (MyColumn = 1) 754 | ) 755 | AND ( 756 | ( 757 | (SomeOtherColumn = 2); 758 | WARNING: unclosed parentheses or section 759 | 760 | SELECT 761 | * 762 | LIMIT 763 | 1; 764 | SELECT 765 | a, 766 | b, 767 | c, 768 | d 769 | FROM 770 | e 771 | LIMIT 772 | 1, 2; 773 | SELECT 774 | 1, 775 | 2, 776 | 3 777 | WHERE 778 | a in (1, 2, 3, 4, 5) 779 | and b = 5; 780 | 781 | SELECT 782 | count - 50 783 | WHERE 784 | a - 50 = b 785 | WHERE 786 | 1 787 | and -50 788 | WHERE 789 | -50 = a 790 | WHERE 791 | a = -50 792 | WHERE 793 | 1 794 | /*test*/ 795 | -50 796 | WHERE 797 | 1 798 | and -50; 799 | 800 | SELECT 801 | @ 802 | and b; 803 | 804 | SELECT 805 | @"weird variable name"; 806 | 807 | SELECT 808 | "no closing quote 809 | 810 | SELECT 811 | [sqlserver] 812 | FROM 813 | [escap[e]]d style]; -------------------------------------------------------------------------------- /tests/compress.html: -------------------------------------------------------------------------------- 1 | SELECT customer_id, customer_name, COUNT(order_id) as total FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id GROUP BY customer_id, customer_name HAVING COUNT(order_id) > 5 ORDER BY COUNT(order_id) DESC; 2 | 3 | UPDATE customers SET totalorders = ordersummary.total FROM (SELECT customer_id, count(order_id) As total FROM orders GROUP BY customer_id) As ordersummary WHERE customers.customer_id = ordersummary.customer_id 4 | 5 | SELECT * FROM sometable UNION ALL SELECT * FROM someothertable; 6 | 7 | SET NAMES 'utf8'; 8 | 9 | CREATE TABLE `PREFIX_address` ( `id_address` int(10) unsigned NOT NULL auto_increment, `id_country` int(10) unsigned NOT NULL, `id_state` int(10) unsigned default NULL, `id_customer` int(10) unsigned NOT NULL default '0', `id_manufacturer` int(10) unsigned NOT NULL default '0', `id_supplier` int(10) unsigned NOT NULL default '0', `id_warehouse` int(10) unsigned NOT NULL default '0', `alias` varchar(32) NOT NULL, `company` varchar(64) default NULL, `lastname` varchar(32) NOT NULL, `firstname` varchar(32) NOT NULL, `address1` varchar(128) NOT NULL, `address2` varchar(128) default NULL, `postcode` varchar(12) default NULL, `city` varchar(64) NOT NULL, `other` text, `phone` varchar(16) default NULL, `phone_mobile` varchar(16) default NULL, `vat_number` varchar(32) default NULL, `dni` varchar(16) DEFAULT NULL, `date_add` datetime NOT NULL, `date_upd` datetime NOT NULL, `active` tinyint(1) unsigned NOT NULL default '1', `deleted` tinyint(1) unsigned NOT NULL default '0', PRIMARY KEY (`id_address`), KEY `address_customer` (`id_customer`), KEY `id_country` (`id_country`), KEY `id_state` (`id_state`), KEY `id_manufacturer` (`id_manufacturer`), KEY `id_supplier` (`id_supplier`), KEY `id_warehouse` (`id_warehouse`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 10 | 11 | CREATE TABLE `PREFIX_alias` ( `id_alias` int(10) unsigned NOT NULL auto_increment, `alias` varchar(255) NOT NULL, `search` varchar(255) NOT NULL, `active` tinyint(1) NOT NULL default '1', PRIMARY KEY (`id_alias`), UNIQUE KEY `alias` (`alias`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 12 | 13 | CREATE TABLE `PREFIX_carrier` ( `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, `id_reference` int(10) unsigned NOT NULL, `id_tax_rules_group` int(10) unsigned DEFAULT '0', `name` varchar(64) NOT NULL, `url` varchar(255) DEFAULT NULL, `active` tinyint(1) unsigned NOT NULL DEFAULT '0', `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', `external_module_name` varchar(64) DEFAULT NULL, `shipping_method` int(2) NOT NULL DEFAULT '0', `position` int(10) unsigned NOT NULL default '0', `max_width` int(10) DEFAULT 0, `max_height` int(10) DEFAULT 0, `max_depth` int(10) DEFAULT 0, `max_weight` int(10) DEFAULT 0, `grade` int(10) DEFAULT 0, PRIMARY KEY (`id_carrier`), KEY `deleted` (`deleted`,`active`), KEY `id_tax_rules_group` (`id_tax_rules_group`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 14 | 15 | CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `id_shop` int(11) unsigned NOT NULL DEFAULT '1', `id_currency` int(10) unsigned NOT NULL, `id_country` int(10) unsigned NOT NULL, `id_group` int(10) unsigned NOT NULL, `from_quantity` mediumint(8) unsigned NOT NULL, `price` DECIMAL(20,6), `reduction` decimal(20,6) NOT NULL, `reduction_type` enum('amount','percentage') NOT NULL, `from` datetime NOT NULL, `to` datetime NOT NULL, PRIMARY KEY (`id_specific_price_rule`), KEY `id_product` (`id_shop`,`id_currency`,`id_country`,`id_group`,`from_quantity`,`from`,`to`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 16 | 17 | UPDATE `PREFIX_configuration` SET value = '6' WHERE name = 'PS_SEARCH_WEIGHT_PNAME' 18 | 19 | UPDATE `PREFIX_hook_module` SET position = 1 WHERE id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPayment') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPaymentReturn') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayHome') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAuthentication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionShopDataDuplication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayTop') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocklanguages') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCustomerAccountAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayCustomerAccount') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsModules') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsvisits') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGraphEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'graphvisifire') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGridEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'gridhtml') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayLeftColumnProduct') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocksharefb') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionSearch') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statssearch') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryUpdate') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryDelete') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAdminMetaSave') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayMyAccountBlock') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayFooter') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockreinsurance') 20 | 21 | ALTER TABLE `PREFIX_employee` ADD `bo_color` varchar(32) default NULL AFTER `stats_date_to` 22 | 23 | INSERT INTO `PREFIX_cms_category_lang` VALUES(1, 3, 'Inicio', '', 'home', NULL, NULL, NULL) 24 | 25 | INSERT INTO `PREFIX_cms_category` VALUES(1, 0, 0, 1, NOW(), NOW(),0) 26 | 27 | UPDATE `PREFIX_cms_category` SET `position` = 0 28 | 29 | ALTER TABLE `PREFIX_customer` ADD `note` text AFTER `secure_key` 30 | 31 | ALTER TABLE `PREFIX_contact` ADD `customer_service` tinyint(1) NOT NULL DEFAULT 0 AFTER `email` 32 | 33 | INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) ( SELECT dq.`id_product`, 1, 1, 0, 1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100), IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) ) 34 | 35 | DROP TABLE `PREFIX_discount_quantity` 36 | 37 | INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) ( SELECT p.`id_product`, 1, 0, 0, 0, 0, 0.00, 1, IF(p.`reduction_price` > 0, p.`reduction_price`, p.`reduction_percent` / 100), IF(p.`reduction_price` > 0, 'amount', 'percentage'), IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_from`), IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_to`) FROM `PREFIX_product` p WHERE p.`reduction_price` OR p.`reduction_percent` ) 38 | 39 | ALTER TABLE `PREFIX_product` DROP `reduction_price`, DROP `reduction_percent`, DROP `reduction_from`, DROP `reduction_to` 40 | 41 | INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_SPECIFIC_PRICE_PRIORITIES', 'id_shop;id_currency;id_country;id_group', NOW(), NOW()), ('PS_TAX_DISPLAY', 0, NOW(), NOW()), ('PS_SMARTY_FORCE_COMPILE', 1, NOW(), NOW()), ('PS_DISTANCE_UNIT', 'km', NOW(), NOW()), ('PS_STORES_DISPLAY_CMS', 0, NOW(), NOW()), ('PS_STORES_DISPLAY_FOOTER', 0, NOW(), NOW()), ('PS_STORES_SIMPLIFIED', 0, NOW(), NOW()), ('PS_STATSDATA_CUSTOMER_PAGESVIEWS', 1, NOW(), NOW()), ('PS_STATSDATA_PAGESVIEWS', 1, NOW(), NOW()), ('PS_STATSDATA_PLUGINS', 1, NOW(), NOW()) 42 | 43 | INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_CONDITIONS_CMS_ID', IFNULL((SELECT `id_cms` FROM `PREFIX_cms` WHERE `id_cms` = 3), 0), NOW(), NOW()) 44 | 45 | CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` ( `value` text ) 46 | 47 | SET @defaultOOS = (SELECT value FROM `PREFIX_configuration` WHERE name = 'PS_ORDER_OUT_OF_STOCK') 48 | 49 | UPDATE `PREFIX_product` p SET `cache_default_attribute` = 0 WHERE `id_product` NOT IN (SELECT `id_product` FROM `PREFIX_product_attribute`) 50 | 51 | INSERT INTO `PREFIX_hook` (`name`, `title`, `description`, `position`) VALUES ('processCarrier', 'Carrier Process', NULL, 0) 52 | 53 | INSERT INTO `PREFIX_stock_mvt_reason_lang` (`id_stock_mvt_reason`, `id_lang`, `name`) VALUES (1, 1, 'Order'), (1, 2, 'Commande'), (2, 1, 'Missing Stock Movement'), (2, 2, 'Mouvement de stock manquant'), (3, 1, 'Restocking'), (3, 2, 'Réassort') 54 | 55 | INSERT INTO `PREFIX_meta_lang` (`id_lang`, `id_meta`, `title`, `url_rewrite`) VALUES (1, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentication', 'authentication'), (2, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentification', 'authentification'), (3, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Autenticación', 'autenticacion') 56 | 57 | LOCK TABLES `admin_assert` WRITE 58 | 59 | UNLOCK TABLES 60 | 61 | DROP TABLE IF EXISTS `admin_role` 62 | 63 | SELECT * FROM MyTable WHERE 1 = 2; 64 | 65 | SELECT 66 | 67 | SELECT Test FROM Test WHERE ( MyColumn = 1 )) AND ((( SomeOtherColumn = 2); 68 | 69 | SELECT * LIMIT 1; SELECT a,b,c,d FROM e LIMIT 1, 2; SELECT 1,2,3 WHERE a in (1,2,3,4,5) and b=5; 70 | 71 | SELECT count - 50 WHERE a-50 = b WHERE 1 and - 50 WHERE -50 = a WHERE a = -50 WHERE 1 - 50 WHERE 1 and -50; 72 | 73 | SELECT @ and b; 74 | 75 | SELECT @"weird variable name"; 76 | 77 | SELECT "no closing quote 78 | 79 | SELECT [sqlserver] FROM [escap[e]]d style]; -------------------------------------------------------------------------------- /tests/format.html: -------------------------------------------------------------------------------- 1 | SELECT 2 | customer_id, 3 | customer_name, 4 | COUNT(order_id) as total 5 | FROM 6 | customers 7 | INNER JOIN orders ON customers.customer_id = orders.customer_id 8 | GROUP BY 9 | customer_id, 10 | customer_name 11 | HAVING 12 | COUNT(order_id) > 5 13 | ORDER BY 14 | COUNT(order_id) DESC; 15 | 16 | UPDATE 17 | customers 18 | SET 19 | totalorders = ordersummary.total 20 | FROM 21 | ( 22 | SELECT 23 | customer_id, 24 | count(order_id) As total 25 | FROM 26 | orders 27 | GROUP BY 28 | customer_id 29 | ) As ordersummary 30 | WHERE 31 | customers.customer_id = ordersummary.customer_id 32 | 33 | SELECT 34 | * 35 | FROM 36 | sometable 37 | UNION ALL 38 | SELECT 39 | * 40 | FROM 41 | someothertable; 42 | 43 | SET 44 | NAMES 'utf8'; 45 | 46 | CREATE TABLE `PREFIX_address` ( 47 | `id_address` int(10) unsigned NOT NULL auto_increment, 48 | `id_country` int(10) unsigned NOT NULL, 49 | `id_state` int(10) unsigned default NULL, 50 | `id_customer` int(10) unsigned NOT NULL default '0', 51 | `id_manufacturer` int(10) unsigned NOT NULL default '0', 52 | `id_supplier` int(10) unsigned NOT NULL default '0', 53 | `id_warehouse` int(10) unsigned NOT NULL default '0', 54 | `alias` varchar(32) NOT NULL, 55 | `company` varchar(64) default NULL, 56 | `lastname` varchar(32) NOT NULL, 57 | `firstname` varchar(32) NOT NULL, 58 | `address1` varchar(128) NOT NULL, 59 | `address2` varchar(128) default NULL, 60 | `postcode` varchar(12) default NULL, 61 | `city` varchar(64) NOT NULL, 62 | `other` text, 63 | `phone` varchar(16) default NULL, 64 | `phone_mobile` varchar(16) default NULL, 65 | `vat_number` varchar(32) default NULL, 66 | `dni` varchar(16) DEFAULT NULL, 67 | `date_add` datetime NOT NULL, 68 | `date_upd` datetime NOT NULL, 69 | `active` tinyint(1) unsigned NOT NULL default '1', 70 | `deleted` tinyint(1) unsigned NOT NULL default '0', 71 | PRIMARY KEY (`id_address`), 72 | KEY `address_customer` (`id_customer`), 73 | KEY `id_country` (`id_country`), 74 | KEY `id_state` (`id_state`), 75 | KEY `id_manufacturer` (`id_manufacturer`), 76 | KEY `id_supplier` (`id_supplier`), 77 | KEY `id_warehouse` (`id_warehouse`) 78 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 79 | 80 | CREATE TABLE `PREFIX_alias` ( 81 | `id_alias` int(10) unsigned NOT NULL auto_increment, 82 | `alias` varchar(255) NOT NULL, 83 | `search` varchar(255) NOT NULL, 84 | `active` tinyint(1) NOT NULL default '1', 85 | PRIMARY KEY (`id_alias`), 86 | UNIQUE KEY `alias` (`alias`) 87 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 88 | 89 | CREATE TABLE `PREFIX_carrier` ( 90 | `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, 91 | `id_reference` int(10) unsigned NOT NULL, 92 | `id_tax_rules_group` int(10) unsigned DEFAULT '0', 93 | `name` varchar(64) NOT NULL, 94 | `url` varchar(255) DEFAULT NULL, 95 | `active` tinyint(1) unsigned NOT NULL DEFAULT '0', 96 | `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', 97 | `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', 98 | `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', 99 | `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', 100 | `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', 101 | `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', 102 | `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', 103 | `external_module_name` varchar(64) DEFAULT NULL, 104 | `shipping_method` int(2) NOT NULL DEFAULT '0', 105 | `position` int(10) unsigned NOT NULL default '0', 106 | `max_width` int(10) DEFAULT 0, 107 | `max_height` int(10) DEFAULT 0, 108 | `max_depth` int(10) DEFAULT 0, 109 | `max_weight` int(10) DEFAULT 0, 110 | `grade` int(10) DEFAULT 0, 111 | PRIMARY KEY (`id_carrier`), 112 | KEY `deleted` (`deleted`, `active`), 113 | KEY `id_tax_rules_group` (`id_tax_rules_group`) 114 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 115 | 116 | CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( 117 | `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, 118 | `name` VARCHAR(255) NOT NULL, 119 | `id_shop` int(11) unsigned NOT NULL DEFAULT '1', 120 | `id_currency` int(10) unsigned NOT NULL, 121 | `id_country` int(10) unsigned NOT NULL, 122 | `id_group` int(10) unsigned NOT NULL, 123 | `from_quantity` mediumint(8) unsigned NOT NULL, 124 | `price` DECIMAL(20, 6), 125 | `reduction` decimal(20, 6) NOT NULL, 126 | `reduction_type` enum('amount', 'percentage') NOT NULL, 127 | `from` datetime NOT NULL, 128 | `to` datetime NOT NULL, 129 | PRIMARY KEY (`id_specific_price_rule`), 130 | KEY `id_product` ( 131 | `id_shop`, `id_currency`, `id_country`, 132 | `id_group`, `from_quantity`, `from`, 133 | `to` 134 | ) 135 | ) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 136 | 137 | UPDATE 138 | `PREFIX_configuration` 139 | SET 140 | value = '6' 141 | WHERE 142 | name = 'PS_SEARCH_WEIGHT_PNAME' 143 | 144 | UPDATE 145 | `PREFIX_hook_module` 146 | SET 147 | position = 1 148 | WHERE 149 | id_hook = ( 150 | SELECT 151 | id_hook 152 | FROM 153 | `PREFIX_hook` 154 | WHERE 155 | name = 'displayPayment' 156 | ) 157 | AND id_module = ( 158 | SELECT 159 | id_module 160 | FROM 161 | `PREFIX_module` 162 | WHERE 163 | name = 'cheque' 164 | ) 165 | OR id_hook = ( 166 | SELECT 167 | id_hook 168 | FROM 169 | `PREFIX_hook` 170 | WHERE 171 | name = 'displayPaymentReturn' 172 | ) 173 | AND id_module = ( 174 | SELECT 175 | id_module 176 | FROM 177 | `PREFIX_module` 178 | WHERE 179 | name = 'cheque' 180 | ) 181 | OR id_hook = ( 182 | SELECT 183 | id_hook 184 | FROM 185 | `PREFIX_hook` 186 | WHERE 187 | name = 'displayHome' 188 | ) 189 | AND id_module = ( 190 | SELECT 191 | id_module 192 | FROM 193 | `PREFIX_module` 194 | WHERE 195 | name = 'homeslider' 196 | ) 197 | OR id_hook = ( 198 | SELECT 199 | id_hook 200 | FROM 201 | `PREFIX_hook` 202 | WHERE 203 | name = 'actionAuthentication' 204 | ) 205 | AND id_module = ( 206 | SELECT 207 | id_module 208 | FROM 209 | `PREFIX_module` 210 | WHERE 211 | name = 'statsdata' 212 | ) 213 | OR id_hook = ( 214 | SELECT 215 | id_hook 216 | FROM 217 | `PREFIX_hook` 218 | WHERE 219 | name = 'actionShopDataDuplication' 220 | ) 221 | AND id_module = ( 222 | SELECT 223 | id_module 224 | FROM 225 | `PREFIX_module` 226 | WHERE 227 | name = 'homeslider' 228 | ) 229 | OR id_hook = ( 230 | SELECT 231 | id_hook 232 | FROM 233 | `PREFIX_hook` 234 | WHERE 235 | name = 'displayTop' 236 | ) 237 | AND id_module = ( 238 | SELECT 239 | id_module 240 | FROM 241 | `PREFIX_module` 242 | WHERE 243 | name = 'blocklanguages' 244 | ) 245 | OR id_hook = ( 246 | SELECT 247 | id_hook 248 | FROM 249 | `PREFIX_hook` 250 | WHERE 251 | name = 'actionCustomerAccountAdd' 252 | ) 253 | AND id_module = ( 254 | SELECT 255 | id_module 256 | FROM 257 | `PREFIX_module` 258 | WHERE 259 | name = 'statsdata' 260 | ) 261 | OR id_hook = ( 262 | SELECT 263 | id_hook 264 | FROM 265 | `PREFIX_hook` 266 | WHERE 267 | name = 'displayCustomerAccount' 268 | ) 269 | AND id_module = ( 270 | SELECT 271 | id_module 272 | FROM 273 | `PREFIX_module` 274 | WHERE 275 | name = 'favoriteproducts' 276 | ) 277 | OR id_hook = ( 278 | SELECT 279 | id_hook 280 | FROM 281 | `PREFIX_hook` 282 | WHERE 283 | name = 'displayAdminStatsModules' 284 | ) 285 | AND id_module = ( 286 | SELECT 287 | id_module 288 | FROM 289 | `PREFIX_module` 290 | WHERE 291 | name = 'statsvisits' 292 | ) 293 | OR id_hook = ( 294 | SELECT 295 | id_hook 296 | FROM 297 | `PREFIX_hook` 298 | WHERE 299 | name = 'displayAdminStatsGraphEngine' 300 | ) 301 | AND id_module = ( 302 | SELECT 303 | id_module 304 | FROM 305 | `PREFIX_module` 306 | WHERE 307 | name = 'graphvisifire' 308 | ) 309 | OR id_hook = ( 310 | SELECT 311 | id_hook 312 | FROM 313 | `PREFIX_hook` 314 | WHERE 315 | name = 'displayAdminStatsGridEngine' 316 | ) 317 | AND id_module = ( 318 | SELECT 319 | id_module 320 | FROM 321 | `PREFIX_module` 322 | WHERE 323 | name = 'gridhtml' 324 | ) 325 | OR id_hook = ( 326 | SELECT 327 | id_hook 328 | FROM 329 | `PREFIX_hook` 330 | WHERE 331 | name = 'displayLeftColumnProduct' 332 | ) 333 | AND id_module = ( 334 | SELECT 335 | id_module 336 | FROM 337 | `PREFIX_module` 338 | WHERE 339 | name = 'blocksharefb' 340 | ) 341 | OR id_hook = ( 342 | SELECT 343 | id_hook 344 | FROM 345 | `PREFIX_hook` 346 | WHERE 347 | name = 'actionSearch' 348 | ) 349 | AND id_module = ( 350 | SELECT 351 | id_module 352 | FROM 353 | `PREFIX_module` 354 | WHERE 355 | name = 'statssearch' 356 | ) 357 | OR id_hook = ( 358 | SELECT 359 | id_hook 360 | FROM 361 | `PREFIX_hook` 362 | WHERE 363 | name = 'actionCategoryAdd' 364 | ) 365 | AND id_module = ( 366 | SELECT 367 | id_module 368 | FROM 369 | `PREFIX_module` 370 | WHERE 371 | name = 'blockcategories' 372 | ) 373 | OR id_hook = ( 374 | SELECT 375 | id_hook 376 | FROM 377 | `PREFIX_hook` 378 | WHERE 379 | name = 'actionCategoryUpdate' 380 | ) 381 | AND id_module = ( 382 | SELECT 383 | id_module 384 | FROM 385 | `PREFIX_module` 386 | WHERE 387 | name = 'blockcategories' 388 | ) 389 | OR id_hook = ( 390 | SELECT 391 | id_hook 392 | FROM 393 | `PREFIX_hook` 394 | WHERE 395 | name = 'actionCategoryDelete' 396 | ) 397 | AND id_module = ( 398 | SELECT 399 | id_module 400 | FROM 401 | `PREFIX_module` 402 | WHERE 403 | name = 'blockcategories' 404 | ) 405 | OR id_hook = ( 406 | SELECT 407 | id_hook 408 | FROM 409 | `PREFIX_hook` 410 | WHERE 411 | name = 'actionAdminMetaSave' 412 | ) 413 | AND id_module = ( 414 | SELECT 415 | id_module 416 | FROM 417 | `PREFIX_module` 418 | WHERE 419 | name = 'blockcategories' 420 | ) 421 | OR id_hook = ( 422 | SELECT 423 | id_hook 424 | FROM 425 | `PREFIX_hook` 426 | WHERE 427 | name = 'displayMyAccountBlock' 428 | ) 429 | AND id_module = ( 430 | SELECT 431 | id_module 432 | FROM 433 | `PREFIX_module` 434 | WHERE 435 | name = 'favoriteproducts' 436 | ) 437 | OR id_hook = ( 438 | SELECT 439 | id_hook 440 | FROM 441 | `PREFIX_hook` 442 | WHERE 443 | name = 'displayFooter' 444 | ) 445 | AND id_module = ( 446 | SELECT 447 | id_module 448 | FROM 449 | `PREFIX_module` 450 | WHERE 451 | name = 'blockreinsurance' 452 | ) 453 | 454 | ALTER TABLE 455 | `PREFIX_employee` 456 | ADD 457 | `bo_color` varchar(32) default NULL 458 | AFTER 459 | `stats_date_to` 460 | 461 | INSERT INTO `PREFIX_cms_category_lang` 462 | VALUES 463 | ( 464 | 1, 3, 'Inicio', '', 'home', NULL, NULL, 465 | NULL 466 | ) 467 | 468 | INSERT INTO `PREFIX_cms_category` 469 | VALUES 470 | (1, 0, 0, 1, NOW(), NOW(), 0) 471 | 472 | UPDATE 473 | `PREFIX_cms_category` 474 | SET 475 | `position` = 0 476 | 477 | ALTER TABLE 478 | `PREFIX_customer` 479 | ADD 480 | `note` text 481 | AFTER 482 | `secure_key` 483 | 484 | ALTER TABLE 485 | `PREFIX_contact` 486 | ADD 487 | `customer_service` tinyint(1) NOT NULL DEFAULT 0 488 | AFTER 489 | `email` 490 | 491 | INSERT INTO `PREFIX_specific_price` ( 492 | `id_product`, `id_shop`, `id_currency`, 493 | `id_country`, `id_group`, `priority`, 494 | `price`, `from_quantity`, `reduction`, 495 | `reduction_type`, `from`, `to` 496 | ) ( 497 | SELECT 498 | dq.`id_product`, 499 | 1, 500 | 1, 501 | 0, 502 | 1, 503 | 0, 504 | 0.00, 505 | dq.`quantity`, 506 | IF( 507 | dq.`id_discount_type` = 2, dq.`value`, 508 | dq.`value` / 100 509 | ), 510 | IF ( 511 | dq.`id_discount_type` = 2, 'amount', 512 | 'percentage' 513 | ), 514 | '0000-00-00 00:00:00', 515 | '0000-00-00 00:00:00' 516 | FROM 517 | `PREFIX_discount_quantity` dq 518 | INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) 519 | ) 520 | 521 | DROP 522 | TABLE `PREFIX_discount_quantity` 523 | 524 | INSERT INTO `PREFIX_specific_price` ( 525 | `id_product`, `id_shop`, `id_currency`, 526 | `id_country`, `id_group`, `priority`, 527 | `price`, `from_quantity`, `reduction`, 528 | `reduction_type`, `from`, `to` 529 | ) ( 530 | SELECT 531 | p.`id_product`, 532 | 1, 533 | 0, 534 | 0, 535 | 0, 536 | 0, 537 | 0.00, 538 | 1, 539 | IF( 540 | p.`reduction_price` > 0, p.`reduction_price`, 541 | p.`reduction_percent` / 100 542 | ), 543 | IF( 544 | p.`reduction_price` > 0, 'amount', 545 | 'percentage' 546 | ), 547 | IF ( 548 | p.`reduction_from` = p.`reduction_to`, 549 | '0000-00-00 00:00:00', p.`reduction_from` 550 | ), 551 | IF ( 552 | p.`reduction_from` = p.`reduction_to`, 553 | '0000-00-00 00:00:00', p.`reduction_to` 554 | ) 555 | FROM 556 | `PREFIX_product` p 557 | WHERE 558 | p.`reduction_price` 559 | OR p.`reduction_percent` 560 | ) 561 | 562 | ALTER TABLE 563 | `PREFIX_product` 564 | DROP 565 | `reduction_price`, 566 | DROP 567 | `reduction_percent`, 568 | DROP 569 | `reduction_from`, 570 | DROP 571 | `reduction_to` 572 | 573 | INSERT INTO `PREFIX_configuration` ( 574 | `name`, `value`, `date_add`, `date_upd` 575 | ) 576 | VALUES 577 | ( 578 | 'PS_SPECIFIC_PRICE_PRIORITIES', 579 | 'id_shop;id_currency;id_country;id_group', 580 | NOW(), NOW() 581 | ), 582 | ('PS_TAX_DISPLAY', 0, NOW(), NOW()), 583 | ( 584 | 'PS_SMARTY_FORCE_COMPILE', 1, NOW(), 585 | NOW() 586 | ), 587 | ( 588 | 'PS_DISTANCE_UNIT', 'km', NOW(), NOW() 589 | ), 590 | ( 591 | 'PS_STORES_DISPLAY_CMS', 0, NOW(), 592 | NOW() 593 | ), 594 | ( 595 | 'PS_STORES_DISPLAY_FOOTER', 0, NOW(), 596 | NOW() 597 | ), 598 | ( 599 | 'PS_STORES_SIMPLIFIED', 0, NOW(), 600 | NOW() 601 | ), 602 | ( 603 | 'PS_STATSDATA_CUSTOMER_PAGESVIEWS', 604 | 1, NOW(), NOW() 605 | ), 606 | ( 607 | 'PS_STATSDATA_PAGESVIEWS', 1, NOW(), 608 | NOW() 609 | ), 610 | ( 611 | 'PS_STATSDATA_PLUGINS', 1, NOW(), 612 | NOW() 613 | ) 614 | 615 | INSERT INTO `PREFIX_configuration` ( 616 | `name`, `value`, `date_add`, `date_upd` 617 | ) 618 | VALUES 619 | ( 620 | 'PS_CONDITIONS_CMS_ID', 621 | IFNULL( 622 | ( 623 | SELECT 624 | `id_cms` 625 | FROM 626 | `PREFIX_cms` 627 | WHERE 628 | `id_cms` = 3 629 | ), 630 | 0 631 | ), 632 | NOW(), 633 | NOW() 634 | ) 635 | 636 | CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (`value` text) 637 | 638 | SET 639 | @defaultOOS = ( 640 | SELECT 641 | value 642 | FROM 643 | `PREFIX_configuration` 644 | WHERE 645 | name = 'PS_ORDER_OUT_OF_STOCK' 646 | ) 647 | 648 | UPDATE 649 | `PREFIX_product` p 650 | SET 651 | `cache_default_attribute` = 0 652 | WHERE 653 | `id_product` NOT IN ( 654 | SELECT 655 | `id_product` 656 | FROM 657 | `PREFIX_product_attribute` 658 | ) 659 | 660 | INSERT INTO `PREFIX_hook` ( 661 | `name`, `title`, `description`, `position` 662 | ) 663 | VALUES 664 | ( 665 | 'processCarrier', 'Carrier Process', 666 | NULL, 0 667 | ) 668 | 669 | INSERT INTO `PREFIX_stock_mvt_reason_lang` ( 670 | `id_stock_mvt_reason`, `id_lang`, 671 | `name` 672 | ) 673 | VALUES 674 | (1, 1, 'Order'), 675 | (1, 2, 'Commande'), 676 | (2, 1, 'Missing Stock Movement'), 677 | ( 678 | 2, 2, 'Mouvement de stock manquant' 679 | ), 680 | (3, 1, 'Restocking'), 681 | (3, 2, 'Réassort') 682 | 683 | INSERT INTO `PREFIX_meta_lang` ( 684 | `id_lang`, `id_meta`, `title`, `url_rewrite` 685 | ) 686 | VALUES 687 | ( 688 | 1, 689 | ( 690 | SELECT 691 | `id_meta` 692 | FROM 693 | `PREFIX_meta` 694 | WHERE 695 | `page` = 'authentication' 696 | ), 697 | 'Authentication', 698 | 'authentication' 699 | ), 700 | ( 701 | 2, 702 | ( 703 | SELECT 704 | `id_meta` 705 | FROM 706 | `PREFIX_meta` 707 | WHERE 708 | `page` = 'authentication' 709 | ), 710 | 'Authentification', 711 | 'authentification' 712 | ), 713 | ( 714 | 3, 715 | ( 716 | SELECT 717 | `id_meta` 718 | FROM 719 | `PREFIX_meta` 720 | WHERE 721 | `page` = 'authentication' 722 | ), 723 | 'Autenticación', 724 | 'autenticacion' 725 | ) 726 | 727 | LOCK TABLES `admin_assert` WRITE 728 | 729 | UNLOCK TABLES 730 | 731 | DROP 732 | TABLE IF EXISTS `admin_role` 733 | 734 | SELECT 735 | * 736 | FROM 737 | -- This is another comment 738 | MyTable # One final comment 739 | 740 | /* This is a block comment 741 | */ 742 | WHERE 743 | 1 = 2; 744 | 745 | SELECT 746 | -- This is a test 747 | 748 | SELECT 749 | Test 750 | FROM 751 | Test 752 | WHERE 753 | (MyColumn = 1) 754 | ) 755 | AND ( 756 | ( 757 | (SomeOtherColumn = 2); 758 | 759 | SELECT 760 | * 761 | LIMIT 762 | 1; 763 | SELECT 764 | a, 765 | b, 766 | c, 767 | d 768 | FROM 769 | e 770 | LIMIT 771 | 1, 2; 772 | SELECT 773 | 1, 774 | 2, 775 | 3 776 | WHERE 777 | a in (1, 2, 3, 4, 5) 778 | and b = 5; 779 | 780 | SELECT 781 | count - 50 782 | WHERE 783 | a - 50 = b 784 | WHERE 785 | 1 786 | and -50 787 | WHERE 788 | -50 = a 789 | WHERE 790 | a = -50 791 | WHERE 792 | 1 793 | /*test*/ 794 | -50 795 | WHERE 796 | 1 797 | and -50; 798 | 799 | SELECT 800 | @ 801 | and b; 802 | 803 | SELECT 804 | @"weird variable name"; 805 | 806 | SELECT 807 | "no closing quote 808 | 809 | SELECT 810 | [sqlserver] 811 | FROM 812 | [escap[e]]d style]; -------------------------------------------------------------------------------- /tests/highlight.html: -------------------------------------------------------------------------------- 1 |
    SELECT customer_id, customer_name, COUNT(order_id) as total
      2 | FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id
      3 | GROUP BY customer_id, customer_name
      4 | HAVING COUNT(order_id) > 5
      5 | ORDER BY COUNT(order_id) DESC;
    6 | 7 |
    UPDATE customers
      8 |         SET totalorders = ordersummary.total
      9 |         FROM (SELECT customer_id, count(order_id) As total 
     10 | FROM orders GROUP BY customer_id) As ordersummary
     11 |         WHERE customers.customer_id = ordersummary.customer_id
    12 | 13 |
    SELECT * FROM sometable
     14 | UNION ALL
     15 | SELECT * FROM someothertable;
    16 | 17 |
    SET NAMES 'utf8';
    18 | 19 |
    CREATE TABLE `PREFIX_address` (
     20 |   `id_address` int(10) unsigned NOT NULL auto_increment,
     21 |   `id_country` int(10) unsigned NOT NULL,
     22 |   `id_state` int(10) unsigned default NULL,
     23 |   `id_customer` int(10) unsigned NOT NULL default '0',
     24 |   `id_manufacturer` int(10) unsigned NOT NULL default '0',
     25 |   `id_supplier` int(10) unsigned NOT NULL default '0',
     26 |   `id_warehouse` int(10) unsigned NOT NULL default '0',
     27 |   `alias` varchar(32) NOT NULL,
     28 |   `company` varchar(64) default NULL,
     29 |   `lastname` varchar(32) NOT NULL,
     30 |   `firstname` varchar(32) NOT NULL,
     31 |   `address1` varchar(128) NOT NULL,
     32 |   `address2` varchar(128) default NULL,
     33 |   `postcode` varchar(12) default NULL,
     34 |   `city` varchar(64) NOT NULL,
     35 |   `other` text,
     36 |   `phone` varchar(16) default NULL,
     37 |   `phone_mobile` varchar(16) default NULL,
     38 |   `vat_number` varchar(32) default NULL,
     39 |   `dni` varchar(16) DEFAULT NULL,
     40 |   `date_add` datetime NOT NULL,
     41 |   `date_upd` datetime NOT NULL,
     42 |   `active` tinyint(1) unsigned NOT NULL default '1',
     43 |   `deleted` tinyint(1) unsigned NOT NULL default '0',
     44 |   PRIMARY KEY (`id_address`),
     45 |   KEY `address_customer` (`id_customer`),
     46 |   KEY `id_country` (`id_country`),
     47 |   KEY `id_state` (`id_state`),
     48 |   KEY `id_manufacturer` (`id_manufacturer`),
     49 |   KEY `id_supplier` (`id_supplier`),
     50 |   KEY `id_warehouse` (`id_warehouse`)
     51 | ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8
    52 | 53 |
    CREATE TABLE `PREFIX_alias` (
     54 |   `id_alias` int(10) unsigned NOT NULL auto_increment,
     55 |   `alias` varchar(255) NOT NULL,
     56 |   `search` varchar(255) NOT NULL,
     57 |   `active` tinyint(1) NOT NULL default '1',
     58 |   PRIMARY KEY (`id_alias`),
     59 |   UNIQUE KEY `alias` (`alias`)
     60 | ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8
    61 | 62 |
    CREATE TABLE `PREFIX_carrier` (
     63 |   `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT,
     64 |   `id_reference` int(10) unsigned NOT NULL,
     65 |   `id_tax_rules_group` int(10) unsigned DEFAULT '0',
     66 |   `name` varchar(64) NOT NULL,
     67 |   `url` varchar(255) DEFAULT NULL,
     68 |   `active` tinyint(1) unsigned NOT NULL DEFAULT '0',
     69 |   `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
     70 |   `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1',
     71 |   `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0',
     72 |   `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0',
     73 |   `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0',
     74 |   `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0',
     75 |   `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0',
     76 |   `external_module_name` varchar(64) DEFAULT NULL,
     77 |   `shipping_method` int(2) NOT NULL DEFAULT '0',
     78 |   `position` int(10) unsigned NOT NULL default '0',
     79 |   `max_width` int(10) DEFAULT 0,
     80 |   `max_height` int(10)  DEFAULT 0,
     81 |   `max_depth` int(10)  DEFAULT 0,
     82 |   `max_weight` int(10)  DEFAULT 0,
     83 |   `grade` int(10)  DEFAULT 0,
     84 |   PRIMARY KEY (`id_carrier`),
     85 |   KEY `deleted` (`deleted`,`active`),
     86 |   KEY `id_tax_rules_group` (`id_tax_rules_group`)
     87 | ) ENGINE=ENGINE_TYPE  DEFAULT CHARSET=utf8
    88 | 89 |
    CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` (
     90 | 	`id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT,
     91 | 	`name` VARCHAR(255) NOT NULL,
     92 | 	`id_shop` int(11) unsigned NOT NULL DEFAULT '1',
     93 | 	`id_currency` int(10) unsigned NOT NULL,
     94 | 	`id_country` int(10) unsigned NOT NULL,
     95 | 	`id_group` int(10) unsigned NOT NULL,
     96 | 	`from_quantity` mediumint(8) unsigned NOT NULL,
     97 | 	`price` DECIMAL(20,6),
     98 | 	`reduction` decimal(20,6) NOT NULL,
     99 | 	`reduction_type` enum('amount','percentage') NOT NULL,
    100 | 	`from` datetime NOT NULL,
    101 | 	`to` datetime NOT NULL,
    102 | 	PRIMARY KEY (`id_specific_price_rule`),
    103 | 	KEY `id_product` (`id_shop`,`id_currency`,`id_country`,`id_group`,`from_quantity`,`from`,`to`)
    104 | ) ENGINE=ENGINE_TYPE  DEFAULT CHARSET=utf8
    105 | 106 |
    UPDATE `PREFIX_configuration` SET value = '6' WHERE name = 'PS_SEARCH_WEIGHT_PNAME'
    107 | 108 |
    UPDATE `PREFIX_hook_module` SET position = 1
    109 | WHERE
    110 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPayment') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque')
    111 | 	OR
    112 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPaymentReturn') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque')
    113 | 	OR
    114 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayHome') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider')
    115 | 	OR
    116 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAuthentication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata')
    117 | 	OR
    118 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionShopDataDuplication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider')
    119 | 	OR
    120 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayTop') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocklanguages')
    121 | 	OR
    122 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCustomerAccountAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata')
    123 | 	OR
    124 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayCustomerAccount') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts')
    125 | 	OR
    126 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsModules') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsvisits')
    127 | 	OR
    128 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGraphEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'graphvisifire')
    129 | 	OR
    130 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGridEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'gridhtml')
    131 | 	OR
    132 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayLeftColumnProduct') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocksharefb')
    133 | 	OR
    134 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionSearch') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statssearch')
    135 | 	OR
    136 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    137 | 	OR
    138 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryUpdate') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    139 | 	OR
    140 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryDelete') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    141 | 	OR
    142 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAdminMetaSave') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    143 | 	OR
    144 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayMyAccountBlock') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts')
    145 | 	OR
    146 | 	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayFooter') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockreinsurance')
    147 | 148 |
    ALTER TABLE `PREFIX_employee` ADD `bo_color` varchar(32) default NULL AFTER `stats_date_to`
    149 | 150 |
    INSERT INTO `PREFIX_cms_category_lang` VALUES(1, 3, 'Inicio', '', 'home', NULL, NULL, NULL)
    151 | 152 |
    INSERT INTO `PREFIX_cms_category` VALUES(1, 0, 0, 1, NOW(), NOW(),0)
    153 | 154 |
    UPDATE `PREFIX_cms_category` SET `position` = 0
    155 | 156 |
    ALTER TABLE `PREFIX_customer` ADD `note` text AFTER `secure_key`
    157 | 158 |
    ALTER TABLE `PREFIX_contact` ADD `customer_service` tinyint(1) NOT NULL DEFAULT 0 AFTER `email`
    159 | 160 |
    INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`)
    161 | 	(	SELECT dq.`id_product`, 1, 1, 0, 1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100), IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00'
    162 | 		FROM `PREFIX_discount_quantity` dq
    163 | 		INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)
    164 | 	)
    165 | 166 |
    DROP TABLE `PREFIX_discount_quantity`
    167 | 168 |
    INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) (
    169 | 	SELECT
    170 | 		p.`id_product`,
    171 | 		1,
    172 | 		0,
    173 | 		0,
    174 | 		0,
    175 | 		0,
    176 | 		0.00,
    177 | 		1,
    178 | 		IF(p.`reduction_price` > 0, p.`reduction_price`, p.`reduction_percent` / 100),
    179 | 		IF(p.`reduction_price` > 0, 'amount', 'percentage'),
    180 | 		IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_from`),
    181 | 		IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_to`)
    182 | 	FROM `PREFIX_product` p
    183 | 	WHERE p.`reduction_price` OR p.`reduction_percent`
    184 | )
    185 | 186 |
    ALTER TABLE `PREFIX_product`
    187 | 	DROP `reduction_price`,
    188 | 	DROP `reduction_percent`,
    189 | 	DROP `reduction_from`,
    190 | 	DROP `reduction_to`
    191 | 192 |
    INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES
    193 | ('PS_SPECIFIC_PRICE_PRIORITIES', 'id_shop;id_currency;id_country;id_group', NOW(), NOW()),
    194 | ('PS_TAX_DISPLAY', 0, NOW(), NOW()),
    195 | ('PS_SMARTY_FORCE_COMPILE', 1, NOW(), NOW()),
    196 | ('PS_DISTANCE_UNIT', 'km', NOW(), NOW()),
    197 | ('PS_STORES_DISPLAY_CMS', 0, NOW(), NOW()),
    198 | ('PS_STORES_DISPLAY_FOOTER', 0, NOW(), NOW()),
    199 | ('PS_STORES_SIMPLIFIED', 0, NOW(), NOW()),
    200 | ('PS_STATSDATA_CUSTOMER_PAGESVIEWS', 1, NOW(), NOW()),
    201 | ('PS_STATSDATA_PAGESVIEWS', 1, NOW(), NOW()),
    202 | ('PS_STATSDATA_PLUGINS', 1, NOW(), NOW())
    203 | 204 |
    INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_CONDITIONS_CMS_ID', IFNULL((SELECT `id_cms` FROM `PREFIX_cms` WHERE `id_cms` = 3), 0), NOW(), NOW())
    205 | 206 |
    CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (
    207 | 	`value` text
    208 | )
    209 | 210 |
    SET @defaultOOS = (SELECT value FROM `PREFIX_configuration` WHERE name = 'PS_ORDER_OUT_OF_STOCK')
    211 | 212 |
    UPDATE `PREFIX_product` p SET `cache_default_attribute` =  0 WHERE `id_product` NOT IN (SELECT `id_product` FROM `PREFIX_product_attribute`)
    213 | 214 |
    INSERT INTO `PREFIX_hook` (`name`, `title`, `description`, `position`) VALUES ('processCarrier', 'Carrier Process', NULL, 0)
    215 | 216 |
    INSERT INTO `PREFIX_stock_mvt_reason_lang` (`id_stock_mvt_reason`, `id_lang`, `name`) VALUES
    217 | (1, 1, 'Order'),
    218 | (1, 2, 'Commande'),
    219 | (2, 1, 'Missing Stock Movement'),
    220 | (2, 2, 'Mouvement de stock manquant'),
    221 | (3, 1, 'Restocking'),
    222 | (3, 2, 'Réassort')
    223 | 224 |
    INSERT INTO `PREFIX_meta_lang` (`id_lang`, `id_meta`, `title`, `url_rewrite`) VALUES
    225 | (1, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentication', 'authentication'),
    226 | (2, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentification', 'authentification'),
    227 | (3, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Autenticación', 'autenticacion')
    228 | 229 |
    LOCK TABLES `admin_assert` WRITE
    230 | 231 |
    UNLOCK TABLES
    232 | 233 |
    DROP TABLE IF EXISTS `admin_role`
    234 | 235 |
    SELECT * FROM
    236 | -- This is another comment
    237 | MyTable # One final comment
    238 | /* This is a block comment 
    239 | */ WHERE 1 = 2;
    240 | 241 |
    SELECT -- This is a test
    242 | 243 |
    SELECT Test FROM Test WHERE
    244 | (
    245 |  MyColumn = 1 )) AND ((( SomeOtherColumn = 2);
    246 | 247 |
    SELECT * LIMIT 1; SELECT a,b,c,d FROM e LIMIT 1, 2; SELECT 1,2,3 WHERE a in (1,2,3,4,5) and b=5;
    248 | 249 |
    SELECT count - 50
    250 | WHERE a-50 = b
    251 | WHERE 1 and - 50
    252 | WHERE -50 = a
    253 | WHERE a = -50
    254 | WHERE 1 /*test*/ - 50
    255 | WHERE 1 and -50;
    256 | 257 |
    SELECT @ and b;
    258 | 259 |
    SELECT @"weird variable name";
    260 | 261 |
    SELECT "no closing quote
    262 | 263 |
    SELECT [sqlserver] FROM [escap[e]]d style];
    -------------------------------------------------------------------------------- /tests/performance.php: -------------------------------------------------------------------------------- 1 | Formatted ".$num." queries using a max_cachekey_size of ".SqlFormatter::$max_cachekey_size."

    "; 36 | 37 | echo "

    Average query length of ".number_format($chars/$num,5)." characters

    "; 38 | 39 | echo "

    Took ".number_format($end-$start,5)." seconds total, ".number_format(($end-$start)/$num,5)." seconds per query, ".number_format(1000*($end-$start)/$chars,5)." seconds per 1000 characters

    "; 40 | 41 | echo "

    Used ".number_format($uend-$ustart)." bytes of memory

    "; 42 | 43 | echo "

    Cache Stats

    ".print_r(SqlFormatter::getCacheStats(),true)."
    "; 44 | 45 | -------------------------------------------------------------------------------- /tests/sql.sql: -------------------------------------------------------------------------------- 1 | SELECT customer_id, customer_name, COUNT(order_id) as total 2 | FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id 3 | GROUP BY customer_id, customer_name 4 | HAVING COUNT(order_id) > 5 5 | ORDER BY COUNT(order_id) DESC; 6 | 7 | UPDATE customers 8 | SET totalorders = ordersummary.total 9 | FROM (SELECT customer_id, count(order_id) As total 10 | FROM orders GROUP BY customer_id) As ordersummary 11 | WHERE customers.customer_id = ordersummary.customer_id 12 | 13 | SELECT * FROM sometable 14 | UNION ALL 15 | SELECT * FROM someothertable; 16 | 17 | SET NAMES 'utf8'; 18 | 19 | CREATE TABLE `PREFIX_address` ( 20 | `id_address` int(10) unsigned NOT NULL auto_increment, 21 | `id_country` int(10) unsigned NOT NULL, 22 | `id_state` int(10) unsigned default NULL, 23 | `id_customer` int(10) unsigned NOT NULL default '0', 24 | `id_manufacturer` int(10) unsigned NOT NULL default '0', 25 | `id_supplier` int(10) unsigned NOT NULL default '0', 26 | `id_warehouse` int(10) unsigned NOT NULL default '0', 27 | `alias` varchar(32) NOT NULL, 28 | `company` varchar(64) default NULL, 29 | `lastname` varchar(32) NOT NULL, 30 | `firstname` varchar(32) NOT NULL, 31 | `address1` varchar(128) NOT NULL, 32 | `address2` varchar(128) default NULL, 33 | `postcode` varchar(12) default NULL, 34 | `city` varchar(64) NOT NULL, 35 | `other` text, 36 | `phone` varchar(16) default NULL, 37 | `phone_mobile` varchar(16) default NULL, 38 | `vat_number` varchar(32) default NULL, 39 | `dni` varchar(16) DEFAULT NULL, 40 | `date_add` datetime NOT NULL, 41 | `date_upd` datetime NOT NULL, 42 | `active` tinyint(1) unsigned NOT NULL default '1', 43 | `deleted` tinyint(1) unsigned NOT NULL default '0', 44 | PRIMARY KEY (`id_address`), 45 | KEY `address_customer` (`id_customer`), 46 | KEY `id_country` (`id_country`), 47 | KEY `id_state` (`id_state`), 48 | KEY `id_manufacturer` (`id_manufacturer`), 49 | KEY `id_supplier` (`id_supplier`), 50 | KEY `id_warehouse` (`id_warehouse`) 51 | ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 52 | 53 | CREATE TABLE `PREFIX_alias` ( 54 | `id_alias` int(10) unsigned NOT NULL auto_increment, 55 | `alias` varchar(255) NOT NULL, 56 | `search` varchar(255) NOT NULL, 57 | `active` tinyint(1) NOT NULL default '1', 58 | PRIMARY KEY (`id_alias`), 59 | UNIQUE KEY `alias` (`alias`) 60 | ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 61 | 62 | CREATE TABLE `PREFIX_carrier` ( 63 | `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, 64 | `id_reference` int(10) unsigned NOT NULL, 65 | `id_tax_rules_group` int(10) unsigned DEFAULT '0', 66 | `name` varchar(64) NOT NULL, 67 | `url` varchar(255) DEFAULT NULL, 68 | `active` tinyint(1) unsigned NOT NULL DEFAULT '0', 69 | `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', 70 | `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', 71 | `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', 72 | `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', 73 | `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', 74 | `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', 75 | `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', 76 | `external_module_name` varchar(64) DEFAULT NULL, 77 | `shipping_method` int(2) NOT NULL DEFAULT '0', 78 | `position` int(10) unsigned NOT NULL default '0', 79 | `max_width` int(10) DEFAULT 0, 80 | `max_height` int(10) DEFAULT 0, 81 | `max_depth` int(10) DEFAULT 0, 82 | `max_weight` int(10) DEFAULT 0, 83 | `grade` int(10) DEFAULT 0, 84 | PRIMARY KEY (`id_carrier`), 85 | KEY `deleted` (`deleted`,`active`), 86 | KEY `id_tax_rules_group` (`id_tax_rules_group`) 87 | ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 88 | 89 | CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( 90 | `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, 91 | `name` VARCHAR(255) NOT NULL, 92 | `id_shop` int(11) unsigned NOT NULL DEFAULT '1', 93 | `id_currency` int(10) unsigned NOT NULL, 94 | `id_country` int(10) unsigned NOT NULL, 95 | `id_group` int(10) unsigned NOT NULL, 96 | `from_quantity` mediumint(8) unsigned NOT NULL, 97 | `price` DECIMAL(20,6), 98 | `reduction` decimal(20,6) NOT NULL, 99 | `reduction_type` enum('amount','percentage') NOT NULL, 100 | `from` datetime NOT NULL, 101 | `to` datetime NOT NULL, 102 | PRIMARY KEY (`id_specific_price_rule`), 103 | KEY `id_product` (`id_shop`,`id_currency`,`id_country`,`id_group`,`from_quantity`,`from`,`to`) 104 | ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 105 | 106 | UPDATE `PREFIX_configuration` SET value = '6' WHERE name = 'PS_SEARCH_WEIGHT_PNAME' 107 | 108 | UPDATE `PREFIX_hook_module` SET position = 1 109 | WHERE 110 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPayment') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') 111 | OR 112 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPaymentReturn') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') 113 | OR 114 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayHome') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') 115 | OR 116 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAuthentication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') 117 | OR 118 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionShopDataDuplication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') 119 | OR 120 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayTop') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocklanguages') 121 | OR 122 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCustomerAccountAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') 123 | OR 124 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayCustomerAccount') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') 125 | OR 126 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsModules') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsvisits') 127 | OR 128 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGraphEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'graphvisifire') 129 | OR 130 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGridEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'gridhtml') 131 | OR 132 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayLeftColumnProduct') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocksharefb') 133 | OR 134 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionSearch') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statssearch') 135 | OR 136 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') 137 | OR 138 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryUpdate') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') 139 | OR 140 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryDelete') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') 141 | OR 142 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAdminMetaSave') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') 143 | OR 144 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayMyAccountBlock') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') 145 | OR 146 | id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayFooter') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockreinsurance') 147 | 148 | ALTER TABLE `PREFIX_employee` ADD `bo_color` varchar(32) default NULL AFTER `stats_date_to` 149 | 150 | INSERT INTO `PREFIX_cms_category_lang` VALUES(1, 3, 'Inicio', '', 'home', NULL, NULL, NULL) 151 | 152 | INSERT INTO `PREFIX_cms_category` VALUES(1, 0, 0, 1, NOW(), NOW(),0) 153 | 154 | UPDATE `PREFIX_cms_category` SET `position` = 0 155 | 156 | ALTER TABLE `PREFIX_customer` ADD `note` text AFTER `secure_key` 157 | 158 | ALTER TABLE `PREFIX_contact` ADD `customer_service` tinyint(1) NOT NULL DEFAULT 0 AFTER `email` 159 | 160 | INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) 161 | ( SELECT dq.`id_product`, 1, 1, 0, 1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100), IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' 162 | FROM `PREFIX_discount_quantity` dq 163 | INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) 164 | ) 165 | 166 | DROP TABLE `PREFIX_discount_quantity` 167 | 168 | INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) ( 169 | SELECT 170 | p.`id_product`, 171 | 1, 172 | 0, 173 | 0, 174 | 0, 175 | 0, 176 | 0.00, 177 | 1, 178 | IF(p.`reduction_price` > 0, p.`reduction_price`, p.`reduction_percent` / 100), 179 | IF(p.`reduction_price` > 0, 'amount', 'percentage'), 180 | IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_from`), 181 | IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_to`) 182 | FROM `PREFIX_product` p 183 | WHERE p.`reduction_price` OR p.`reduction_percent` 184 | ) 185 | 186 | ALTER TABLE `PREFIX_product` 187 | DROP `reduction_price`, 188 | DROP `reduction_percent`, 189 | DROP `reduction_from`, 190 | DROP `reduction_to` 191 | 192 | INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES 193 | ('PS_SPECIFIC_PRICE_PRIORITIES', 'id_shop;id_currency;id_country;id_group', NOW(), NOW()), 194 | ('PS_TAX_DISPLAY', 0, NOW(), NOW()), 195 | ('PS_SMARTY_FORCE_COMPILE', 1, NOW(), NOW()), 196 | ('PS_DISTANCE_UNIT', 'km', NOW(), NOW()), 197 | ('PS_STORES_DISPLAY_CMS', 0, NOW(), NOW()), 198 | ('PS_STORES_DISPLAY_FOOTER', 0, NOW(), NOW()), 199 | ('PS_STORES_SIMPLIFIED', 0, NOW(), NOW()), 200 | ('PS_STATSDATA_CUSTOMER_PAGESVIEWS', 1, NOW(), NOW()), 201 | ('PS_STATSDATA_PAGESVIEWS', 1, NOW(), NOW()), 202 | ('PS_STATSDATA_PLUGINS', 1, NOW(), NOW()) 203 | 204 | INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_CONDITIONS_CMS_ID', IFNULL((SELECT `id_cms` FROM `PREFIX_cms` WHERE `id_cms` = 3), 0), NOW(), NOW()) 205 | 206 | CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` ( 207 | `value` text 208 | ) 209 | 210 | SET @defaultOOS = (SELECT value FROM `PREFIX_configuration` WHERE name = 'PS_ORDER_OUT_OF_STOCK') 211 | 212 | UPDATE `PREFIX_product` p SET `cache_default_attribute` = 0 WHERE `id_product` NOT IN (SELECT `id_product` FROM `PREFIX_product_attribute`) 213 | 214 | INSERT INTO `PREFIX_hook` (`name`, `title`, `description`, `position`) VALUES ('processCarrier', 'Carrier Process', NULL, 0) 215 | 216 | INSERT INTO `PREFIX_stock_mvt_reason_lang` (`id_stock_mvt_reason`, `id_lang`, `name`) VALUES 217 | (1, 1, 'Order'), 218 | (1, 2, 'Commande'), 219 | (2, 1, 'Missing Stock Movement'), 220 | (2, 2, 'Mouvement de stock manquant'), 221 | (3, 1, 'Restocking'), 222 | (3, 2, 'Réassort') 223 | 224 | INSERT INTO `PREFIX_meta_lang` (`id_lang`, `id_meta`, `title`, `url_rewrite`) VALUES 225 | (1, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentication', 'authentication'), 226 | (2, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentification', 'authentification'), 227 | (3, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Autenticación', 'autenticacion') 228 | 229 | LOCK TABLES `admin_assert` WRITE 230 | 231 | UNLOCK TABLES 232 | 233 | DROP TABLE IF EXISTS `admin_role` 234 | 235 | SELECT * FROM 236 | -- This is another comment 237 | MyTable # One final comment 238 | /* This is a block comment 239 | */ WHERE 1 = 2; 240 | 241 | SELECT -- This is a test 242 | 243 | SELECT Test FROM Test WHERE 244 | ( 245 | MyColumn = 1 )) AND ((( SomeOtherColumn = 2); 246 | 247 | SELECT * LIMIT 1; SELECT a,b,c,d FROM e LIMIT 1, 2; SELECT 1,2,3 WHERE a in (1,2,3,4,5) and b=5; 248 | 249 | SELECT count - 50 250 | WHERE a-50 = b 251 | WHERE 1 and - 50 252 | WHERE -50 = a 253 | WHERE a = -50 254 | WHERE 1 /*test*/ - 50 255 | WHERE 1 and -50; 256 | 257 | SELECT @ and b; 258 | 259 | SELECT @"weird variable name"; 260 | 261 | SELECT "no closing quote 262 | 263 | SELECT [sqlserver] FROM [escap[e]]d style]; 264 | --------------------------------------------------------------------------------