/tools/parser_sql/processors/UnionProcessor.php

https://gitlab.com/staging06/myproject · PHP · 168 lines · 85 code · 21 blank · 62 comment · 14 complexity · a1df0504f7b07fc118286be28399e211 MD5 · raw file

  1. <?php
  2. /**
  3. * UnionProcessor.php
  4. *
  5. * This file implements the processor for the UNION statements.
  6. *
  7. * Copyright (c) 2010-2012, Justin Swanhart
  8. * with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without modification,
  13. * are permitted provided that the following conditions are met:
  14. *
  15. * * Redistributions of source code must retain the above copyright notice,
  16. * this list of conditions and the following disclaimer.
  17. * * Redistributions in binary form must reproduce the above copyright notice,
  18. * this list of conditions and the following disclaimer in the documentation
  19. * and/or other materials provided with the distribution.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  22. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  23. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
  24. * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  26. * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  27. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  28. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  29. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  30. * DAMAGE.
  31. */
  32. require_once(dirname(__FILE__) . '/AbstractProcessor.php');
  33. require_once(dirname(__FILE__) . '/SQLProcessor.php');
  34. require_once(dirname(__FILE__) . '/DefaultProcessor.php');
  35. require_once(dirname(__FILE__) . '/../utils/ExpressionType.php');
  36. /**
  37. *
  38. * This class processes the UNION statements.
  39. *
  40. * @author arothe
  41. *
  42. */
  43. class UnionProcessor extends AbstractProcessor {
  44. public function isUnion($queries) {
  45. $unionTypes = array('UNION', 'UNION ALL');
  46. foreach ($unionTypes as $unionType) {
  47. if (!empty($queries[$unionType])) {
  48. return true;
  49. }
  50. }
  51. return false;
  52. }
  53. /**
  54. * MySQL supports a special form of UNION:
  55. * (select ...)
  56. * union
  57. * (select ...)
  58. *
  59. * This function handles this query syntax. Only one such subquery
  60. * is supported in each UNION block. (select)(select)union(select) is not legal.
  61. * The extra queries will be silently ignored.
  62. */
  63. protected function processMySQLUnion($queries) {
  64. $unionTypes = array('UNION', 'UNION ALL');
  65. foreach ($unionTypes as $unionType) {
  66. if (empty($queries[$unionType])) {
  67. continue;
  68. }
  69. foreach ($queries[$unionType] as $key => $tokenList) {
  70. foreach ($tokenList as $z => $token) {
  71. $token = trim($token);
  72. if ($token === "") {
  73. continue;
  74. }
  75. // starts with "(select"
  76. if (preg_match("/^\\(\\s*select\\s*/i", $token)) {
  77. $processor = new DefaultProcessor();
  78. $queries[$unionType][$key] = $processor->process($this->removeParenthesisFromStart($token));
  79. break;
  80. }
  81. $processor = new SQLProcessor();
  82. $queries[$unionType][$key] = $processor->process($queries[$unionType][$key]);
  83. break;
  84. }
  85. }
  86. }
  87. // it can be parsed or not
  88. return $queries;
  89. }
  90. public function process($inputArray) {
  91. $outputArray = array();
  92. // ometimes the parser needs to skip ahead until a particular
  93. // oken is found
  94. $skipUntilToken = false;
  95. // his is the last type of union used (UNION or UNION ALL)
  96. // ndicates a) presence of at least one union in this query
  97. // b) the type of union if this is the first or last query
  98. $unionType = false;
  99. // ometimes a "query" consists of more than one query (like a UNION query)
  100. // his array holds all the queries
  101. $queries = array();
  102. foreach ($inputArray as $key => $token) {
  103. $trim = trim($token);
  104. // overread all tokens till that given token
  105. if ($skipUntilToken) {
  106. if ($trim === "") {
  107. continue; // read the next token
  108. }
  109. if (strtoupper($trim) === $skipUntilToken) {
  110. $skipUntilToken = false;
  111. continue; // read the next token
  112. }
  113. }
  114. if (strtoupper($trim) !== "UNION") {
  115. $outputArray[] = $token; // here we get empty tokens, if we remove these, we get problems in parse_sql()
  116. continue;
  117. }
  118. $unionType = "UNION";
  119. // we are looking for an ALL token right after UNION
  120. for ($i = $key + 1; $i < count($inputArray); ++$i) {
  121. if (trim($inputArray[$i]) === "") {
  122. continue;
  123. }
  124. if (strtoupper($inputArray[$i]) !== "ALL") {
  125. break;
  126. }
  127. // the other for-loop should overread till "ALL"
  128. $skipUntilToken = "ALL";
  129. $unionType = "UNION ALL";
  130. }
  131. // store the tokens related to the unionType
  132. $queries[$unionType][] = $outputArray;
  133. $outputArray = array();
  134. }
  135. // the query tokens after the last UNION or UNION ALL
  136. // or we don't have an UNION/UNION ALL
  137. if (!empty($outputArray)) {
  138. if ($unionType) {
  139. $queries[$unionType][] = $outputArray;
  140. } else {
  141. $queries[] = $outputArray;
  142. }
  143. }
  144. return $this->processMySQLUnion($queries);
  145. }
  146. }
  147. ?>