Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
column.php
1<?php
2namespace Bitrix\Perfmon\Sql;
3
5
6class Column extends BaseObject
7{
9 public $parent = null;
10 public $type = '';
11 public $typeAddition = '';
12 public $unsigned = false;
13 public $length = '';
14 public $precision = 0;
15 public $nullable = true;
16 public $default = null;
17 public $enum = [];
18
19 protected static $types = [
20 'BIGINT' => true,
21 'BINARY' => true,
22 'BLOB' => true,
23 'BOOLEAN' => true,
24 'BYTEA' => true,
25 'CHAR' => true,
26 'DATE' => true,
27 'DATETIME' => true,
28 'DECIMAL' => true,
29 'DOUBLE' => true,
30 'ENUM' => true,
31 'FLOAT' => true,
32 'INT' => true,
33 'INT8' => true,
34 'INTEGER' => true,
35 'LONGBLOB' => true,
36 'LONGTEXT' => true,
37 'MEDIUMBLOB' => true,
38 'MEDIUMINT' => true,
39 'MEDIUMTEXT' => true,
40 'NUMBER' => true,
41 'NUMERIC' => true,
42 'REAL' => true,
43 'SET' => true,
44 'SMALLINT' => true,
45 'TEXT' => true,
46 'TIME' => true,
47 'TIMESTAMP' => true,
48 'TINYBLOB' => true,
49 'TINYINT' => true,
50 'TINYTEXT' => true,
51 'VARBINARY' => true,
52 'VARCHAR' => true,
53 ];
54
92 public static function checkType($type)
93 {
94 return isset(self::$types[$type]);
95 }
96
106 public function getLength($charWidth, $maxLength = null)
107 {
108 $length = $maxLength ?? intval($this->length);
109 static $fixed = [
110 'INT' => 4,
111 'INTEGER' => 4,
112 'TINYINT' => 1,
113 'FLOAT' => 4,
114 'DOUBLE' => 8,
115 'BIGINT' => 8,
116 'SMALLINT' => 2,
117 'MEDIUMINT' => 3,
118 'TIMESTAMP' => 4,
119 'DATETIME' => 8,
120 'YEAR' => 1,
121 'DATE' => 3,
122 'TIME' => 3,
123 'NUMERIC' => 4, //up to
124 'NUMBER' => 4, //up to
125 'DECIMAL' => 4, //up to
126 'ENUM' => 2, //up to
127 'SET' => 8, //up to
128 'BOOLEAN' => 1,
129 ];
130 if (isset($fixed[$this->type]))
131 {
132 return $fixed[$this->type];
133 }
134 if ($this->type === 'BINARY')
135 {
136 return $length;
137 }
138 if ($this->type === 'VARBINARY')
139 {
140 return $length + ($length > 255 ? 2 : 1);
141 }
142 if ($this->type === 'TINYBLOB' || $this->type === 'TINYTEXT')
143 {
144 return ($length ?: pow(2, 8)) + 1;
145 }
146 if ($this->type === 'BLOB' || $this->type === 'TEXT')
147 {
148 return ($length ?: pow(2, 16)) + 2;
149 }
150 if ($this->type === 'MEDIUMBLOB' || $this->type === 'MEDIUMTEXT')
151 {
152 return ($length ?: pow(2, 24)) + 3;
153 }
154 if ($this->type === 'LONGBLOB' || $this->type === 'LONGTEXT')
155 {
156 return ($length ?: pow(2, 32)) + 3;
157 }
158 if ($this->type === 'CHAR')
159 {
160 return $length * $charWidth;
161 }
162 if ($this->type === 'VARCHAR')
163 {
164 return ($length * $charWidth) + ($length > 255 ? 2 : 1);
165 }
166 throw new NotSupportedException('column type [' . $this->type . '].');
167 }
168
179 public static function create(Tokenizer $tokenizer)
180 {
181 $columnName = $tokenizer->getCurrentToken()->text;
182
183 $tokenizer->nextToken();
184 $tokenizer->skipWhiteSpace();
185 $token = $tokenizer->getCurrentToken();
186
187 $columnType = $token->upper;
188 if (!self::checkType($columnType))
189 {
190 throw new NotSupportedException('column type expected but [' . $tokenizer->getCurrentToken()->text . '] found. line: ' . $tokenizer->getCurrentToken()->line);
191 }
192
193 $column = new self($columnName);
194 $column->type = $columnType;
195
196 $level = $token->level;
197 $lengthLevel = -1;
198 $columnDefinition = '';
199 do
200 {
201 if ($token->level == $level && $token->text === ',')
202 {
203 break;
204 }
205 if ($token->level < $level && $token->text === ')')
206 {
207 break;
208 }
209
210 $columnDefinition .= $token->text;
211
212 if ($token->upper === 'NOT')
213 {
214 $column->nullable = false;
215 }
216 elseif ($token->upper === 'DEFAULT')
217 {
218 $column->default = false;
219 }
220 elseif ($token->upper === 'UNSIGNED')
221 {
222 $column->unsigned = true;
223 }
224 elseif ($token->upper === 'PRECISION')
225 {
226 $column->typeAddition = $token->upper;
227 }
228 elseif ($token->upper === 'VARYING')
229 {
230 $column->typeAddition = $token->upper;
231 }
232 elseif ($column->default === false)
233 {
234 if ($token->type !== Token::T_WHITESPACE && $token->type !== Token::T_COMMENT)
235 {
236 $column->default = $token->text;
237 }
238 }
239
240 $token = $tokenizer->nextToken();
241
242 //parentheses after type
243 if ($lengthLevel == -1)
244 {
245 if ($token->text === '(')
246 {
247 if ($column->type === 'ENUM')
248 {
249 $lengthLevel = $token->level;
250 while (!$tokenizer->endOfInput())
251 {
252 $columnDefinition .= $token->text;
253
254 $token = $tokenizer->nextToken();
255
256 if ($token->level === $lengthLevel && $token->text === ')')
257 {
258 break;
259 }
260
261 if ($token->type == Token::T_SINGLE_QUOTE)
262 {
263 $column->enum[] = trim($token->text, "'");
264 }
265 elseif ($token->type == Token::T_DOUBLE_QUOTE)
266 {
267 $column->enum[] = trim($token->text, '"');
268 }
269 }
270 }
271 else
272 {
273 $lengthLevel = $token->level;
274 while (!$tokenizer->endOfInput())
275 {
276 $columnDefinition .= $token->text;
277
278 $token = $tokenizer->nextToken();
279
280 if ($token->level === $lengthLevel && $token->text === ')')
281 {
282 break;
283 }
284
285 if ($token->type == Token::T_STRING)
286 {
287 if (!$column->length)
288 {
289 $column->length = (int)$token->text;
290 }
291 else
292 {
293 $column->precision = (int)$token->text;
294 }
295 }
296 }
297 }
298 }
299 elseif ($token->type !== Token::T_WHITESPACE && $token->type !== Token::T_COMMENT)
300 {
301 $lengthLevel = 0;
302 }
303 }
304 }
305 while (!$tokenizer->endOfInput());
306
307 $column->setBody($columnDefinition);
308
309 return $column;
310 }
311
317 public function getDdlType()
318 {
319 return $this->type
320 . ($this->typeAddition ? ' ' . $this->typeAddition : '')
321 . ($this->length !== '' ? '(' . $this->length . ($this->precision !== 0 ? ',' . $this->precision : '') . ')' : '');
322 }
323
331 public function getCreateDdl($dbType = '')
332 {
333 switch ($dbType)
334 {
335 case 'MYSQL':
336 case 'MSSQL':
337 return 'ALTER TABLE ' . $this->parent->name . ' ADD ' . $this->name . ' ' . $this->body;
338 case 'PGSQL':
339 return 'ALTER TABLE ' . $this->parent->name . ' ADD COLUMN ' . $this->name . ' ' . $this->body;
340 case 'ORACLE':
341 return 'ALTER TABLE ' . $this->parent->name . ' ADD (' . $this->name . ' ' . $this->body . ')';
342 default:
343 return '// ' . get_class($this) . ':getCreateDdl for database type [' . $dbType . '] not implemented';
344 }
345 }
346
354 public function getDropDdl($dbType = '')
355 {
356 switch ($dbType)
357 {
358 case 'MYSQL':
359 return 'ALTER TABLE ' . $this->parent->name . ' DROP ' . $this->name;
360 case 'MSSQL':
361 case 'PGSQL':
362 return 'ALTER TABLE ' . $this->parent->name . ' DROP COLUMN ' . $this->name;
363 case 'ORACLE':
364 return 'ALTER TABLE ' . $this->parent->name . ' DROP (' . $this->name . ')';
365 default:
366 return '// ' . get_class($this) . ':getDropDdl for database type [' . $dbType . '] not implemented';
367 }
368 }
369
380 public function getModifyDdl(BaseObject $target, $dbType = '')
381 {
382 switch ($dbType)
383 {
384 case 'MYSQL':
385 return 'ALTER TABLE ' . $this->parent->name . ' CHANGE ' . $this->name . ' ' . $target->name . ' ' . $target->body;
386 case 'PGSQL':
387 $alter = [];
388 $sourceType = $this->getDdlType();
389 $targetType = $target->getDdlType();
390 if ($sourceType !== $targetType)
391 {
392 $alter[] = 'ALTER COLUMN ' . $this->name . ' TYPE ' . $targetType;
393 }
394
395 if ($this->default === null)
396 {
397 if ($target->default !== null)
398 {
399 $alter[] = 'ALTER COLUMN ' . $this->name . ' SET DEFAULT ' . $target->default;
400 }
401 }
402 else
403 {
404 if ($target->default === null)
405 {
406 $alter[] = 'ALTER COLUMN ' . $this->name . ' DROP DEFAULT';
407 }
408 elseif ($this->default != $target->default)
409 {
410 $alter[] = 'ALTER COLUMN ' . $this->name . ' SET DEFAULT ' . $target->default;
411 }
412 }
413
414 if ($this->nullable != $target->nullable)
415 {
416 $alter[] = 'ALTER COLUMN ' . $this->name . ' ' . ($target->nullable ? 'DROP' : 'SET') . ' NOT NULL ';
417 }
418
419 if ($alter)
420 {
421 return 'ALTER TABLE ' . $this->parent->name . ' ' . implode(', ', $alter);
422 }
423 else
424 {
425 return '// ' . get_class($this) . ':getModifyDdl for database type [' . $dbType . "] not implemented. Change requested from [${this}->body] to [${target}->body].";
426 }
427 case 'MSSQL':
428 if ($this->nullable !== $target->nullable)
429 {
430 $nullDdl = ($target->nullable ? ' NULL' : ' NOT NULL');
431 }
432 else
433 {
434 $nullDdl = '';
435 }
436
437 if (
438 $this->type === $target->type
439 && $this->default === $target->default
440 && (
441 intval($this->length) < intval($target->length)
442 || (
443 intval($target->length) < intval($this->length)
444 && mb_strtoupper($this->type) === 'CHAR'
445 )
446 )
447 )
448 {
449 $sql = [];
451 foreach ($this->parent->indexes->getList() as $index)
452 {
453 if (in_array($this->name, $index->columns, true))
454 {
455 $sql[] = $index->getDropDdl($dbType);
456 }
457 }
458 $sql[] = 'ALTER TABLE ' . $this->parent->name . ' ALTER COLUMN ' . $this->name . ' ' . $target->body . $nullDdl;
459 foreach ($this->parent->indexes->getList() as $index)
460 {
461 if (in_array($this->name, $index->columns, true))
462 {
463 $sql[] = $index->getCreateDdl($dbType);
464 }
465 }
466 return $sql;
467 }
468 elseif (
469 $this->type === $target->type
470 && $this->default === $target->default
471 && intval($this->length) === intval($target->length)
472 && $this->nullable !== $target->nullable
473 )
474 {
475 return 'ALTER TABLE ' . $this->parent->name . ' ALTER COLUMN ' . $this->name . ' ' . $target->body;
476 }
477 else
478 {
479 return '// ' . get_class($this) . ':getModifyDdl for database type [' . $dbType . "] not implemented. Change requested from [${this}->body] to [${target}->body].";
480 }
481 case 'ORACLE':
482 if (
483 $this->type === $target->type
484 && $this->default === $target->default
485 && (
486 intval($this->length) < intval($target->length)
487 || (
488 intval($target->length) < intval($this->length)
489 && mb_strtoupper($this->type) === 'CHAR'
490 )
491 )
492 )
493 {
494 return 'ALTER TABLE ' . $this->parent->name . ' MODIFY (' . $this->name . ' ' . $target->type . '(' . $target->length . ')' . ')';
495 }
496 elseif (
497 $this->type === $target->type
498 && $this->default === $target->default
499 && intval($this->length) === intval($target->length)
500 && $this->nullable !== $target->nullable
501 )
502 {
503 return "
504 declare
505 l_nullable varchar2(1);
506 begin
507 select nullable into l_nullable
508 from user_tab_columns
509 where table_name = '" . $this->parent->name . "'
510 and column_name = '" . $this->name . "';
511 if l_nullable = '" . ($target->nullable ? 'N' : 'Y') . "' then
512 execute immediate 'alter table " . $this->parent->name . ' modify (' . $this->name . ' ' . ($target->nullable ? 'NULL' : 'NOT NULL') . ")';
513 end if;
514 end;
515 ";
516 }
517 else
518 {
519 return '// ' . get_class($this) . ':getModifyDdl for database type [' . $dbType . "] not implemented. Change requested from [${this}->body] to [${target}->body].";
520 }
521 default:
522 return '// ' . get_class($this) . ':getModifyDdl for database type [' . $dbType . "] not implemented. Change requested from [${this}->body] to [${target}->body].";
523 }
524 }
525}
getModifyDdl(BaseObject $target, $dbType='')
getCreateDdl($dbType='')
Definition column.php:331
static create(Tokenizer $tokenizer)
Definition column.php:179
getLength($charWidth, $maxLength=null)
Definition column.php:106
static checkType($type)
Definition column.php:92