|
|
@@ -88,30 +88,39 @@ class Core_Pdo extends PDO {
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
+ * TODO: update keys:
|
|
|
* TODO: keys name may be different - try to find and connect with given schema?
|
|
|
* TODO: remove old uniq keys?
|
|
|
*/
|
|
|
public function assertTableStruct($tblName, $expectedStruct, $params = array()) {
|
|
|
- // $expectedStruct['t1'] = array('type'=>'varchar', 'max_length'=>300, 'default_value'=>null)
|
|
|
+ // TODO: make backup for table?
|
|
|
$expectedStruct = $this->_fixExpectedStruct($expectedStruct);
|
|
|
//DBG::_(true, true, "fixedEpectedStruct", $expectedStruct, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
- //DBG::_(true, true, "fixedEpectedStruct", $this->showCreateStructMysql($tblName, $expectedStruct, $params), __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ //DBG::_(true, true, "fixedEpectedStruct", $this->showCreateStruct($tblName, $expectedStruct, $params), __CLASS__, __FUNCTION__, __LINE__);
|
|
|
$struct = $this->getTableStruct($tblName);
|
|
|
- $expectedFields = array_keys($expectedStruct);
|
|
|
+ $expectedFields = array();//array_keys($expectedStruct);
|
|
|
+ foreach ($expectedStruct as $fldName => $fld) {
|
|
|
+ if ('UNIQUE KEY' == $fld['type']) continue;
|
|
|
+ if ('KEY' == $fld['type']) continue;
|
|
|
+ $expectedFields[] = $fldName;
|
|
|
+ }
|
|
|
$currentFields = array_keys($struct);
|
|
|
|
|
|
$missingFields = array_diff($expectedFields, $currentFields);
|
|
|
DBG::_(true, true, "struct", $struct, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
DBG::_(true, true, "missingFields", $missingFields, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
|
|
|
- foreach ($missingFields as $fldName => $expected) {
|
|
|
- DBG::_(true, true, "TODO: add missing field[{$fldName}]:", $expected, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
- $sqlType = array();
|
|
|
- $sqlType['type'] = $expected['type'];
|
|
|
- $sqlType['null'] = ($expected['is_nullable'])? '' : 'NOT NULL';
|
|
|
- $sqlType['default'] = ($expected['is_nullable'])? '' : 'NOT NULL';
|
|
|
- $sqlAdd = "alter table CRM_NOTIFY add {$fldName} {$sqlType['type']} {$sqlType['null']} {$sqlType['default']}";
|
|
|
- DBG::_(true, true, "TODO: sqlChange", $sqlAdd, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ foreach ($missingFields as $fldName) {
|
|
|
+ $fld = $expectedStruct[$fldName];
|
|
|
+ DBG::_(true, true, "add missing field[{$fldName}]:", $fld, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ $sqlFieldStruct = $this->showTableStructField($fldName, $fld);
|
|
|
+ if ($sqlFieldStruct) {
|
|
|
+ $sqlAdd = "alter table {$tblName} add {$sqlFieldStruct}";
|
|
|
+ DBG::_(true, true, "sqlAdd", $sqlAdd, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ $this->exec($sqlAdd);
|
|
|
+ } else {
|
|
|
+ throw new Exception("Unimplemented type '{$fld['type']}': " . json_encode($fld));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
$toUpdateFields = array_intersect($expectedFields, $currentFields);
|
|
|
@@ -120,69 +129,34 @@ class Core_Pdo extends PDO {
|
|
|
$current = $struct[$fldName];
|
|
|
$expected = $expectedStruct[$fldName];
|
|
|
$needChange = false;
|
|
|
- $toChange = array();
|
|
|
- $toChange['type'] = $current['type'];
|
|
|
- $toChange['null'] = ($current['is_nullable'])? '' : 'NOT NULL';
|
|
|
- // TODO: cannot determine when default is NULL
|
|
|
- $toChange['default'] = '';
|
|
|
- if (!empty($current['default_value'])) $toChange['default'] = "DEFAULT '{$current['default_value']}'";
|
|
|
- if (is_null($current['default_value'])) $toChange['default'] = "DEFAULT NULL";
|
|
|
|
|
|
- DBG::_(true, true, "TODO: update field[{$fldName}]:", array('expected'=>$expected,'current'=>$current), __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ $sqlFieldStruct = $this->showTableStructField($fldName, $expected);
|
|
|
+ if (!$sqlFieldStruct) throw new Exception("Unimplemented type '{$expected['type']}' for field '{$fldName}': " . json_encode($expected));
|
|
|
+ $sqlChange = "alter table `{$tblName}` change `{$fldName}` {$sqlFieldStruct}";
|
|
|
+ DBG::_(true, true, "DBG: sqlChange", $sqlChange, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+
|
|
|
+ //DBG::_(true, true, "TODO: update field[{$fldName}]:", array('expected'=>$expected,'current'=>$current), __CLASS__, __FUNCTION__, __LINE__);
|
|
|
if ($current['type'] != $expected['type']) {
|
|
|
- throw new Exception("Unimplemented change field type from {$current['type']} to {$expected['type']}");
|
|
|
+ throw new Exception("Unimplemented change field type from '{$current['type']}' to '{$expected['type']}' for field '{$fldName}': " . json_encode($expected));
|
|
|
}
|
|
|
|
|
|
if ($current['is_nullable'] != $expected['is_nullable']) {
|
|
|
- if (!$current['is_nullable']) {
|
|
|
- $toChange['null'] = '';
|
|
|
+ $needChange = true;
|
|
|
+ if ($current['is_nullable'] && !$expected['is_nullable']) {
|
|
|
+ throw new Exception("Field struct needs change 'is_nullable' to false but this change is not implemented - field '{$fldName}': " . json_encode(array('expected'=>$expected,'current'=>$current)));
|
|
|
}
|
|
|
}
|
|
|
- $sqlChange = "alter table CRM_NOTIFY change {$fldName} {$fldName} {$toChange['type']} {$toChange['null']} {$toChange['default']}";
|
|
|
- if ($needChange) {
|
|
|
- DBG::_(true, true, "TODO: sqlChange", $sqlChange, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
- }
|
|
|
- }
|
|
|
- if(0)foreach ($struct as $col) {
|
|
|
- $fldName = $col['Field'];
|
|
|
- if (!array_key_exists($fldName, $expectedStruct)) continue;// skip not defined cols
|
|
|
- $expected = $expectedStruct[$fldName];
|
|
|
- $toChange = array();
|
|
|
- $toChange['need_change'] = false;
|
|
|
- $toChange['type'] = $col['Type'];
|
|
|
- $toChange['null'] = ('YES' == $col['Null'])? '' : 'NOT NULL';
|
|
|
- // TODO: cannot determine when default is NULL
|
|
|
- $toChange['default'] = (!is_null($col['Default']))? "DEFAULT {$col['Default']}" : '';
|
|
|
-
|
|
|
- $expectedNullable = (array_key_exists('nullable', $expected) && $expected['nullable']);
|
|
|
- $colNullable = ('YES' == $col['Null']);
|
|
|
- if ($expectedNullable != $colNullable) {
|
|
|
- $toChange['null'] = ($expectedNullable)? '' : 'NOT NULL';
|
|
|
- $toChange['need_change'] = true;
|
|
|
- }
|
|
|
-
|
|
|
- // examples:
|
|
|
- // 1 - NOT NULL
|
|
|
- // 2 - NOT NULL DEFAULT ''
|
|
|
- // x 3 - NOT NULL DEFAULT NULL
|
|
|
- // x 4 - NULL
|
|
|
- // 5 - NULL DEFAULT ''
|
|
|
- // 6 - NULL DEFAULT NULL
|
|
|
- // 1 == 2, 4 -> 6
|
|
|
- $expectedHasDefault = array_key_exists('default', $expected);
|
|
|
- $expectedDefaultValue = (array_key_exists('default', $expected))? $expected['default'] : "''";
|
|
|
- $colDefault = $col['Default'];
|
|
|
- if ($expectedDefault != $colDefault) {
|
|
|
- $toChange['default'] = 'DEFAULT ' . ((is_null($expectedDefault))? 'NULL' : $expectedDefault);
|
|
|
- $toChange['need_change'] = true;
|
|
|
+ if ($current['max_length'] != $expected['max_length']) {
|
|
|
+ $needChange = true;
|
|
|
+ if ($current['max_length'] > $expected['max_length']) {
|
|
|
+ throw new Exception("Field struct needs decrease 'max_length' but this change is not implemented - field '{$fldName}': " . json_encode(array('expected'=>$expected,'current'=>$current)));
|
|
|
+ }
|
|
|
}
|
|
|
+ if ($current['default_value'] !== $expected['default_value']) $needChange = true;
|
|
|
|
|
|
- // ALTER TABLE `CRM_NOTIFY` CHANGE `who` `who` VARCHAR(20) NOT NULL
|
|
|
- // ALTER TABLE `CRM_NOTIFY` CHANGE `who` `who` VARCHAR(20) NULL
|
|
|
- // ALTER TABLE `CRM_NOTIFY` CHANGE `who` `who` VARCHAR(20) NULL DEFAULT NULL
|
|
|
- $sqlChange = "alter table CRM_NOTIFY change {$fldName} {$fldName} {$toChange['type']} {$toChange['null']} {$toChange['default']}";
|
|
|
- if ($toChange['need_change']) {
|
|
|
- DBG::_(true, true, "TODO: sqlChange", $sqlChange, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ if ($needChange) {
|
|
|
+ DBG::_(true, true, "EXEC sqlChange ...", $sqlChange, __CLASS__, __FUNCTION__, __LINE__);
|
|
|
+ $this->exec($sqlChange);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -323,87 +297,17 @@ class Core_Pdo extends PDO {
|
|
|
return $fixedStruct;
|
|
|
}
|
|
|
|
|
|
- public function showCreateStructMysql($tblName, $struct, $params = array()) {
|
|
|
+ public function showCreateStruct($tblName, $struct, $params = array()) {
|
|
|
+ // TODO: check database type, if == MySQL - fix construct
|
|
|
$expectedStruct = $this->_fixExpectedStruct($struct);
|
|
|
$linesSql = array();
|
|
|
$dbgSql = array();
|
|
|
foreach ($expectedStruct as $fldName => $fld) {
|
|
|
- $nullSql = ($fld['is_nullable'])? '' : 'NOT NULL';
|
|
|
- $defaultSql = (is_null($fld['default_value']))? 'DEFAULT NULL' : "DEFAULT '{$fld['default_value']}'";
|
|
|
- if (is_null($fld['default_value']) && !$fld['is_nullable']) $defaultSql = '';
|
|
|
- switch ($fld['type']) {
|
|
|
- case 'char':
|
|
|
- case 'varchar': $linesSql[] = "`{$fldName}` {$fld['type']}({$fld['max_length']}) {$nullSql} {$defaultSql}"; break;
|
|
|
- case 'text':
|
|
|
- case 'tinytext':
|
|
|
- case 'longtext':
|
|
|
- case 'mediumtext': $linesSql[] = "`{$fldName}` {$fld['type']} {$nullSql}"; break;
|
|
|
- case 'time':
|
|
|
- case 'timestamp':
|
|
|
- case 'year':
|
|
|
- case 'date':
|
|
|
- case 'datetime': $linesSql[] = "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}"; break;
|
|
|
-// -- Unimplemented type 'int': {"type":"int","is_nullable":false,"default_is_null":false,"default_value":null,"max_length":null,"num_precision":10,"num_scale":0,"char_encoding":null,"char_collation":null,"extra":null}
|
|
|
-// -- Unimplemented type 'int': {"type":"int","num_precision":11,"default_value":null,"is_nullable":true,"default_is_null":false,"max_length":null,"num_scale":0,"char_encoding":null,"char_collation":null,"extra":null}
|
|
|
- case 'int':
|
|
|
- case 'tinyint':
|
|
|
- case 'smallint':
|
|
|
- case 'mediumint':
|
|
|
- case 'bigint':
|
|
|
- if ($fld['num_precision'] > 0) {
|
|
|
- $typeParamsSql = "(" . ($fld['num_precision'] + 1) . ")";
|
|
|
- }
|
|
|
- //if ($fld['num_scale']) $typeParamsSql = ",{$fld['num_scale']}";
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']}{$typeParamsSql} {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'decimal':
|
|
|
- $typeParamsSql = "{$fld['num_precision']},{$fld['num_scale']}";
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']}({$typeParamsSql}) {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'float':
|
|
|
- case 'double':
|
|
|
- case 'real':
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'enum':
|
|
|
- case 'set':
|
|
|
- $typeParamsSql = "'" . implode("','", $fld['values']) . "'";
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']}({$typeParamsSql}) {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'bit':
|
|
|
- case 'binary':
|
|
|
- case 'varbinary':
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']}({$fld['max_length']}) {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'boolean':
|
|
|
- case 'serial':
|
|
|
- case 'blob':
|
|
|
- case 'tinyblob':
|
|
|
- case 'mediumblob':
|
|
|
- case 'longblob':
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'geometry':
|
|
|
- case 'point':
|
|
|
- case 'linestring':
|
|
|
- case 'polygon':
|
|
|
- case 'multipoint':
|
|
|
- case 'multilinestring':
|
|
|
- case 'multipolygon':
|
|
|
- case 'geometrycollection':
|
|
|
- $linesSql[] = "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}";
|
|
|
- break;
|
|
|
- case 'UNIQUE KEY':
|
|
|
- $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`";
|
|
|
- $linesSql[] = "UNIQUE KEY `{$fldName}` ({$keyFieldsSql})";
|
|
|
- break;
|
|
|
- case 'KEY':
|
|
|
- $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`";
|
|
|
- $linesSql[] = "KEY `{$fldName}` ({$keyFieldsSql})";
|
|
|
- break;
|
|
|
-// $expectedStruct['uniq_key_1'] = array('type'=>'UNIQUE KEY', 'key_fields'=>array('who','when','what'));// UNIQUE KEY `uniq_key_1` (`who`,`when`,`what`)
|
|
|
-// $expectedStruct['key_who'] = array('type'=>'KEY', 'key_fields'=>array('who'));// KEY `key_who` (`who`)
|
|
|
- default: $dbgSql[] = "-- Unimplemented type '{$fld['type']}': " . json_encode($fld);
|
|
|
+ $sqlFieldStruct = $this->showTableStructField($fldName, $fld);
|
|
|
+ if ($sqlFieldStruct) {
|
|
|
+ $linesSql[] = $sqlFieldStruct;
|
|
|
+ } else {
|
|
|
+ $dbgSql[] = "-- Unimplemented type '{$fld['type']}': " . json_encode($fld);
|
|
|
}
|
|
|
}
|
|
|
$linesSql = implode("\n\t\t, ", $linesSql);
|
|
|
@@ -418,6 +322,85 @@ EOF_STRUCT_MYSQL;
|
|
|
return $structSql;
|
|
|
}
|
|
|
|
|
|
+ public function showTableStructField($fldName, $fld) {
|
|
|
+ // TODO: check database type, if == MySQL - fix construct
|
|
|
+ $nullSql = ($fld['is_nullable'])? '' : 'NOT NULL';
|
|
|
+ $defaultSql = (is_null($fld['default_value']))? 'DEFAULT NULL' : "DEFAULT '{$fld['default_value']}'";
|
|
|
+ if (is_null($fld['default_value']) && !$fld['is_nullable']) $defaultSql = '';
|
|
|
+ switch ($fld['type']) {
|
|
|
+ case 'char':
|
|
|
+ case 'varchar': return "`{$fldName}` {$fld['type']}({$fld['max_length']}) {$nullSql} {$defaultSql}"; break;
|
|
|
+ case 'text':
|
|
|
+ case 'tinytext':
|
|
|
+ case 'longtext':
|
|
|
+ case 'mediumtext': return "`{$fldName}` {$fld['type']} {$nullSql}"; break;
|
|
|
+ case 'time':
|
|
|
+ case 'timestamp':
|
|
|
+ case 'year':
|
|
|
+ case 'date':
|
|
|
+ case 'datetime': return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}"; break;
|
|
|
+// -- Unimplemented type 'int': {"type":"int","is_nullable":false,"default_is_null":false,"default_value":null,"max_length":null,"num_precision":10,"num_scale":0,"char_encoding":null,"char_collation":null,"extra":null}
|
|
|
+// -- Unimplemented type 'int': {"type":"int","num_precision":11,"default_value":null,"is_nullable":true,"default_is_null":false,"max_length":null,"num_scale":0,"char_encoding":null,"char_collation":null,"extra":null}
|
|
|
+ case 'int':
|
|
|
+ case 'tinyint':
|
|
|
+ case 'smallint':
|
|
|
+ case 'mediumint':
|
|
|
+ case 'bigint':
|
|
|
+ if ($fld['num_precision'] > 0) {
|
|
|
+ $typeParamsSql = "(" . ($fld['num_precision'] + 1) . ")";
|
|
|
+ }
|
|
|
+ //if ($fld['num_scale']) $typeParamsSql = ",{$fld['num_scale']}";
|
|
|
+ return "`{$fldName}` {$fld['type']}{$typeParamsSql} {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'decimal':
|
|
|
+ $typeParamsSql = "{$fld['num_precision']},{$fld['num_scale']}";
|
|
|
+ return "`{$fldName}` {$fld['type']}({$typeParamsSql}) {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'float':
|
|
|
+ case 'double':
|
|
|
+ case 'real':
|
|
|
+ return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'enum':
|
|
|
+ case 'set':
|
|
|
+ $typeParamsSql = "'" . implode("','", $fld['values']) . "'";
|
|
|
+ return "`{$fldName}` {$fld['type']}({$typeParamsSql}) {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'bit':
|
|
|
+ case 'binary':
|
|
|
+ case 'varbinary':
|
|
|
+ return "`{$fldName}` {$fld['type']}({$fld['max_length']}) {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'boolean':
|
|
|
+ case 'serial':
|
|
|
+ case 'blob':
|
|
|
+ case 'tinyblob':
|
|
|
+ case 'mediumblob':
|
|
|
+ case 'longblob':
|
|
|
+ return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'geometry':
|
|
|
+ case 'point':
|
|
|
+ case 'linestring':
|
|
|
+ case 'polygon':
|
|
|
+ case 'multipoint':
|
|
|
+ case 'multilinestring':
|
|
|
+ case 'multipolygon':
|
|
|
+ case 'geometrycollection':
|
|
|
+ return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}";
|
|
|
+ break;
|
|
|
+ case 'UNIQUE KEY':
|
|
|
+ $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`";
|
|
|
+ return "UNIQUE KEY `{$fldName}` ({$keyFieldsSql})";
|
|
|
+ break;
|
|
|
+ case 'KEY':
|
|
|
+ $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`";
|
|
|
+ return "KEY `{$fldName}` ({$keyFieldsSql})";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
public function fetchAll($sql) {
|
|
|
$sth = $this->prepare($sql);
|
|
|
$sth->execute();
|