PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/perfmon/lib/sql/schema.php

https://gitlab.com/alexprowars/bitrix
PHP | 414 lines | 275 code | 30 blank | 109 comment | 29 complexity | cabdb0ba19af9d8cc769fbe08ed48ac7 MD5 | raw file
  1. <?php
  2. namespace Bitrix\Perfmon\Sql;
  3. use Bitrix\Main\NotSupportedException;
  4. //Sample usage:
  5. /*
  6. \Bitrix\Main\Loader::includeModule('perfmon');
  7. $dir = new \Bitrix\Main\IO\Directory("/opt/php03.cp1251.www/mercurial/bitrix/modules");
  8. foreach ($dir->getChildren() as $child)
  9. {
  10. if ($child->isDirectory() && $child->getName()!=='xxx')
  11. {
  12. echo $child->getName(),": ";
  13. foreach (array("mysql"=>";", "mssql"=>"GO", "oracle"=>"/") as $db=>$delimiter)
  14. {
  15. $path = $child->getPath()."/install/db/$db/install.sql";
  16. if (!\Bitrix\Main\IO\File::isFileExists($path))
  17. $path = $child->getPath()."/install/$db/install.sql";
  18. if (!\Bitrix\Main\IO\File::isFileExists($path))
  19. continue;
  20. //echo "<br>$path<br>";
  21. $sql = \Bitrix\Main\IO\File::getFileContents($path);
  22. $s = new \Bitrix\Perfmon\Sql\Schema;
  23. $s->createFromString($sql, $delimiter);
  24. // p r i n t _ r ($s->tables);
  25. echo count($s->tables->getList())," ";
  26. }
  27. echo "\n";
  28. }
  29. }
  30. */
  31. class Schema
  32. {
  33. /** @var Collection */
  34. public $tables = null;
  35. /** @var Collection */
  36. public $procedures = null;
  37. /** @var Collection */
  38. public $sequences = null;
  39. function __construct()
  40. {
  41. $this->tables = new Collection;
  42. $this->procedures = new Collection;
  43. $this->sequences = new Collection;
  44. }
  45. /**
  46. * Fills database schema from DDL text.
  47. *
  48. * @param string $str DDL text.
  49. * @param string $delimiter How to split DDL into statements.
  50. *
  51. * @return void
  52. * @throws NotSupportedException
  53. */
  54. public function createFromString($str, $delimiter)
  55. {
  56. $tokenizer = Tokenizer::createFromString($str);
  57. foreach ($this->splitStatements($tokenizer, $delimiter) as $statement)
  58. {
  59. $this->executeStatement($statement);
  60. }
  61. }
  62. /**
  63. * Splits tokens array into bunch of individual DDL statements.
  64. *
  65. * @param Tokenizer $tokenizer Tokens container.
  66. * @param string $delimiter How to split DDL into statements.
  67. *
  68. * @return array[Tokenizer]
  69. */
  70. protected function splitStatements(Tokenizer $tokenizer, $delimiter = ';')
  71. {
  72. $result = array();
  73. $index = 0;
  74. $result[$index] = array();
  75. /** @var Token $prevToken */
  76. $prevToken = null;
  77. /** @var Token $token */
  78. foreach ($tokenizer->getTokens() as $token)
  79. {
  80. if (
  81. $token->text === $delimiter
  82. && $prevToken
  83. && mb_strpos($prevToken->text, "\n") !== false
  84. )
  85. {
  86. $index++;
  87. $result[$index] = array();
  88. }
  89. elseif (
  90. mb_strpos($token->text, "\n") !== false
  91. && $prevToken
  92. && $prevToken->text === $delimiter
  93. )
  94. {
  95. array_pop($result[$index]);
  96. $index++;
  97. $result[$index] = array();
  98. }
  99. else
  100. {
  101. $result[$index][] = $token;
  102. }
  103. $prevToken = $token;
  104. }
  105. foreach ($result as $i => $tokens)
  106. {
  107. $result[$i] = Tokenizer::createFromTokens($tokens);
  108. }
  109. return $result;
  110. }
  111. /**
  112. * Fills some schema part with information from one DDL statement.
  113. *
  114. * @param Tokenizer $tokenizer Statement tokens.
  115. *
  116. * @return void
  117. * @throws NotSupportedException
  118. */
  119. protected function executeStatement(Tokenizer $tokenizer)
  120. {
  121. /** @var Table $table */
  122. $tokenizer->resetState();
  123. $tokenizer->skipWhiteSpace();
  124. if ($tokenizer->testUpperText('CREATE'))
  125. {
  126. $this->executeCreate($tokenizer);
  127. }
  128. elseif ($tokenizer->testUpperText('INSERT'))
  129. {
  130. //skip insert into
  131. }
  132. elseif ($tokenizer->testUpperText('SET'))
  133. {
  134. //skip set identity_insert
  135. }
  136. elseif ($tokenizer->testUpperText('ALTER'))
  137. {
  138. $this->executeAlter($tokenizer);
  139. }
  140. elseif ($tokenizer->testUpperText('IF'))
  141. {
  142. $tokenizer->skipWhiteSpace();
  143. if ($tokenizer->testUpperText('OBJECT_ID'))
  144. {
  145. while (!$tokenizer->endOfInput())
  146. {
  147. if ($tokenizer->nextToken()->upper === 'CREATE')
  148. break;
  149. }
  150. $tokenizer->nextToken();
  151. $tokenizer->skipWhiteSpace();
  152. if ($tokenizer->testUpperText('TABLE'))
  153. {
  154. $this->executeCreateTable($tokenizer);
  155. }
  156. else
  157. {
  158. throw new NotSupportedException("'CREATE TABLE' expected. line:".$tokenizer->getCurrentToken()->line);
  159. }
  160. }
  161. elseif ($tokenizer->testUpperText('NOT'))
  162. {
  163. $tokenizer->skipWhiteSpace();
  164. if ($tokenizer->testUpperText('EXISTS'))
  165. {
  166. while (!$tokenizer->endOfInput())
  167. {
  168. if ($tokenizer->nextToken()->upper === 'CREATE')
  169. break;
  170. }
  171. $tokenizer->nextToken();
  172. $tokenizer->skipWhiteSpace();
  173. if ($tokenizer->testUpperText('UNIQUE'))
  174. {
  175. $unique = true;
  176. $tokenizer->skipWhiteSpace();
  177. }
  178. else
  179. {
  180. $unique = false;
  181. }
  182. if ($tokenizer->testUpperText('INDEX'))
  183. {
  184. $this->executeCreateIndex($tokenizer, $unique);
  185. }
  186. else
  187. {
  188. throw new NotSupportedException("'CREATE INDEX' expected. line:".$tokenizer->getCurrentToken()->line);
  189. }
  190. }
  191. else
  192. {
  193. throw new NotSupportedException("'NOT EXISTS' expected. line:".$tokenizer->getCurrentToken()->line);
  194. }
  195. }
  196. else
  197. {
  198. throw new NotSupportedException("'OBJECT_ID' expected. line:".$tokenizer->getCurrentToken()->line);
  199. }
  200. }
  201. elseif (!$tokenizer->endOfInput())
  202. {
  203. throw new NotSupportedException("'CREATE' expected. line:".$tokenizer->getCurrentToken()->line);
  204. }
  205. }
  206. /**
  207. * @param Tokenizer $tokenizer Statement tokens.
  208. *
  209. * @return void
  210. * @throws NotSupportedException
  211. */
  212. protected function executeCreate(Tokenizer $tokenizer)
  213. {
  214. $tokenizer->skipWhiteSpace();
  215. if ($tokenizer->testUpperText("OR"))
  216. {
  217. $tokenizer->skipWhiteSpace();
  218. if ($tokenizer->testUpperText("REPLACE"))
  219. $tokenizer->skipWhiteSpace();
  220. else
  221. throw new NotSupportedException("'OR REPLACE' expected. line:".$tokenizer->getCurrentToken()->line);
  222. }
  223. if ($tokenizer->testUpperText('TABLE'))
  224. {
  225. $this->executeCreateTable($tokenizer);
  226. }
  227. elseif ($tokenizer->testUpperText('INDEX'))
  228. {
  229. $this->executeCreateIndex($tokenizer, false);
  230. }
  231. elseif ($tokenizer->testUpperText('UNIQUE'))
  232. {
  233. $tokenizer->skipWhiteSpace();
  234. if ($tokenizer->testUpperText('INDEX'))
  235. $tokenizer->skipWhiteSpace();
  236. $this->executeCreateIndex($tokenizer, true);
  237. }
  238. elseif ($tokenizer->testUpperText('TRIGGER'))
  239. {
  240. $this->executeCreateTrigger($tokenizer);
  241. }
  242. elseif (
  243. $tokenizer->testUpperText('PROCEDURE')
  244. || $tokenizer->testUpperText('FUNCTION')
  245. || $tokenizer->testUpperText('TYPE')
  246. )
  247. {
  248. $this->executeCreateProcedure($tokenizer);
  249. }
  250. elseif ($tokenizer->testUpperText('SEQUENCE'))
  251. {
  252. $this->executeCreateSequence($tokenizer);
  253. }
  254. else
  255. {
  256. throw new NotSupportedException("TABLE|INDEX|UNIQUE|TRIGGER|PROCEDURE|FUNCTION|TYPE|SEQUENCE expected. line:".$tokenizer->getCurrentToken()->line);
  257. }
  258. }
  259. /**
  260. * @param Tokenizer $tokenizer Statement tokens.
  261. *
  262. * @return void
  263. * @throws NotSupportedException
  264. */
  265. protected function executeAlter(Tokenizer $tokenizer)
  266. {
  267. $tokenizer->skipWhiteSpace();
  268. if ($tokenizer->testUpperText('TABLE'))
  269. {
  270. $tokenizer->skipWhiteSpace();
  271. $tableName = $tokenizer->getCurrentToken()->text;
  272. /** @var Table $table */
  273. $table = $this->tables->search($tableName);
  274. if (!$table)
  275. {
  276. throw new NotSupportedException("Table [$tableName] not found. line: ".$tokenizer->getCurrentToken()->line);
  277. }
  278. $tokenizer->nextToken();
  279. $tokenizer->skipWhiteSpace();
  280. if ($tokenizer->testUpperText('ADD'))
  281. {
  282. $tokenizer->skipWhiteSpace();
  283. if ($tokenizer->testUpperText('CONSTRAINT'))
  284. {
  285. $tokenizer->skipWhiteSpace();
  286. $table->createConstraint($tokenizer);
  287. }
  288. }
  289. elseif ($tokenizer->testUpperText('NOCHECK') || $tokenizer->testUpperText('CHECK'))
  290. {
  291. //(NOCHECK|CHECK) CONSTRAINT ALL
  292. }
  293. elseif ($tokenizer->testUpperText('DISABLE') || $tokenizer->testUpperText('ENABLE'))
  294. {
  295. //(DISABLE|ENABLE) TRIGGER ALL
  296. }
  297. else
  298. {
  299. throw new NotSupportedException("'ADD' expected. line:".$tokenizer->getCurrentToken()->line);
  300. }
  301. }
  302. else
  303. {
  304. throw new NotSupportedException("'TABLE' expected. line:".$tokenizer->getCurrentToken()->line);
  305. }
  306. }
  307. /**
  308. * @param Tokenizer $tokenizer Statement tokens.
  309. *
  310. * @return void
  311. * @throws NotSupportedException
  312. */
  313. protected function executeCreateTable(Tokenizer $tokenizer)
  314. {
  315. $tokenizer->skipWhiteSpace();
  316. $this->tables->add(Table::create($tokenizer));
  317. }
  318. /**
  319. * @param Tokenizer $tokenizer Statement tokens.
  320. * @param boolean $unique Index uniqueness flag.
  321. *
  322. * @return void
  323. * @throws NotSupportedException
  324. */
  325. protected function executeCreateIndex(Tokenizer $tokenizer, $unique)
  326. {
  327. $tokenizer->skipWhiteSpace();
  328. $tokenizer->setBookmark();
  329. Index::searchTableName($tokenizer);
  330. $tableName = $tokenizer->getCurrentToken()->text;
  331. /** @var Table $table */
  332. $table = $this->tables->search($tableName);
  333. if (!$table)
  334. {
  335. throw new NotSupportedException("Table [$tableName] not found. line: ".$tokenizer->getCurrentToken()->line);
  336. }
  337. $tokenizer->restoreBookmark();
  338. $table->createIndex($tokenizer, $unique);
  339. }
  340. /**
  341. * @param Tokenizer $tokenizer Statement tokens.
  342. *
  343. * @return void
  344. * @throws NotSupportedException
  345. */
  346. protected function executeCreateTrigger(Tokenizer $tokenizer)
  347. {
  348. $tokenizer->skipWhiteSpace();
  349. $tokenizer->setBookmark();
  350. Trigger::searchTableName($tokenizer);
  351. $tableName = $tokenizer->getCurrentToken()->text;
  352. /** @var Table $table */
  353. $table = $this->tables->search($tableName);
  354. if (!$table)
  355. {
  356. throw new NotSupportedException("Table [$tableName] not found. line: ".$tokenizer->getCurrentToken()->line);
  357. }
  358. $tokenizer->restoreBookmark();
  359. $table->createTrigger($tokenizer);
  360. }
  361. /**
  362. * @param Tokenizer $tokenizer Statement tokens.
  363. *
  364. * @return void
  365. * @throws NotSupportedException
  366. */
  367. protected function executeCreateProcedure(Tokenizer $tokenizer)
  368. {
  369. $tokenizer->putBack();
  370. $this->procedures->add(Procedure::create($tokenizer));
  371. }
  372. /**
  373. * @param Tokenizer $tokenizer Statement tokens.
  374. *
  375. * @return void
  376. * @throws NotSupportedException
  377. */
  378. protected function executeCreateSequence(Tokenizer $tokenizer)
  379. {
  380. $tokenizer->skipWhiteSpace();
  381. $this->sequences->add(Sequence::create($tokenizer));
  382. }
  383. }