diff --git a/application/controllers/CustomvarController.php b/application/controllers/CustomvarController.php index 6e994825..037b5b26 100644 --- a/application/controllers/CustomvarController.php +++ b/application/controllers/CustomvarController.php @@ -69,7 +69,9 @@ class CustomvarController extends CompatController $parent = $this->fetchProperty($parentUuid); if ($parent['parent_uuid'] !== null) { - $usedCount = $this->fetchPropertyUsedCount(Uuid::fromBytes($parent['parent_uuid'])); + $usedCount = $this->fetchPropertyUsedCount(Uuid::fromBytes( + Db\DbUtil::binaryResult($parent['parent_uuid']) + )); } else { $usedCount = $this->fetchPropertyUsedCount($parentUuid); } @@ -84,7 +86,7 @@ class CustomvarController extends CompatController ->select()->from('director_property', 'value_type') ->where( 'parent_uuid = ? AND key_name = \'0\'', - $uuid->getBytes() + Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db) ); $property['item_type'] = $db->fetchOne($itemTypeQuery); @@ -96,7 +98,7 @@ class CustomvarController extends CompatController ->join(['dpl' => 'director_property_datalist'], 'dpl.list_uuid = dl.uuid', []) ->where( 'dpl.property_uuid = ?', - $uuid->getBytes() + Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db) ); $property['list'] = $db->fetchOne($datalistId); @@ -162,7 +164,7 @@ class CustomvarController extends CompatController 'description', 'used_count' => $property['used_count'] > 0 ? 'COUNT(1)' : '0', ]) - ->where('parent_uuid = ?', $uuid->getBytes()) + ->where('parent_uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)) ->group('dp.uuid') ->order('key_name'); @@ -233,12 +235,13 @@ class CustomvarController extends CompatController { $uuid = $this->uuid; $property = $this->fetchProperty($uuid); + $db = $this->db->getDbAdapter(); if (isset($property['parent_uuid'])) { - $parentUuid = Uuid::fromBytes($property['parent_uuid']); + $parentUuid = Uuid::fromBytes(Db\DbUtil::binaryResult($property['parent_uuid'])); $this->parentUuid = $parentUuid; $parentProperty = $this->fetchProperty($parentUuid); if (isset($parentProperty['parent_uuid'])) { - $rootUuid = Uuid::fromBytes($parentProperty['parent_uuid']); + $rootUuid = Uuid::fromBytes(Db\DbUtil::binaryResult($parentProperty['parent_uuid'])); } else { $rootUuid = $parentUuid; } @@ -246,7 +249,6 @@ class CustomvarController extends CompatController $uuid = $rootUuid; } - $db = $this->db->getDbAdapter(); $objectClasses = ['host', 'service', 'notification', 'command', 'user']; $usage = []; @@ -279,11 +281,13 @@ class CustomvarController extends CompatController $columns['host_name'] = 'ioh.object_name'; } - $customPropertyQuery = $customPropertyQuery->columns($columns) - ->where('dp.uuid = ?', $uuid->getBytes()); + $customPropertyQuery = $customPropertyQuery + ->columns($columns) + ->where('dp.uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); - $unionQuery = $unionQuery->columns($columns) - ->where('dp.uuid = ?', $uuid->getBytes()); + $unionQuery = $unionQuery + ->columns($columns) + ->where('dp.uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); $usage[] = $db->fetchAll($db->select()->union([$customPropertyQuery, $unionQuery])); } @@ -379,7 +383,7 @@ class CustomvarController extends CompatController 'label', 'description' ]) - ->where('uuid = ?', $uuid->getBytes()); + ->where('uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); return $db->fetchRow($query, [], Zend_Db::FETCH_ASSOC) ?: []; } @@ -401,7 +405,7 @@ class CustomvarController extends CompatController . ' + COUNT(iup.property_uuid) + COUNT(icp.property_uuid)' . ' + COUNT(inp.property_uuid)' ]) - ->where('uuid = ?', $uuid->getBytes()); + ->where('uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); return (int) $db->fetchOne($query); } diff --git a/application/forms/CustomVariableForm.php b/application/forms/CustomVariableForm.php index ff196078..08dd4a02 100644 --- a/application/forms/CustomVariableForm.php +++ b/application/forms/CustomVariableForm.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Forms; use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\FilterException; use Icinga\Module\Director\Data\Db\DbConnection; +use Icinga\Module\Director\Db; use Icinga\Web\Session; use ipl\I18n\Translation; use ipl\Web\Common\CsrfCounterMeasure; @@ -95,7 +96,7 @@ class CustomVariableForm extends CompatForm $db = $this->db->getDbAdapter(); $query = $db->select() ->from('director_property', ['count' => 'COUNT(*)']) - ->where('parent_uuid = ?', $this->parentUuid->getBytes()); + ->where('parent_uuid = ?', Db\DbUtil::quoteBinaryCompat($this->parentUuid->getBytes(), $db)); $this->addElement( 'hidden', @@ -314,7 +315,7 @@ class CustomVariableForm extends CompatForm 'label', 'description' ]) - ->where('uuid = ?', $uuid->getBytes()); + ->where('uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); return $db->fetchRow($query, [], Zend_Db::FETCH_ASSOC); } @@ -388,27 +389,29 @@ class CustomVariableForm extends CompatForm string $itemType = '' ): void { $this->uuid = Uuid::uuid4(); + $quotedUuid = Db\DbUtil::quoteBinaryCompat($this->uuid->getBytes(), $this->db->getDbAdapter()); $dynamicArrayItemType = []; if ($itemType !== '') { $dynamicArrayItemType = [ - 'uuid' => Uuid::uuid4()->getBytes(), + 'uuid' => Db\DbUtil::quoteBinaryCompat(Uuid::uuid4()->getBytes(), $this->db->getDbAdapter()), 'key_name' => '0', 'value_type' => $itemType, - 'parent_uuid' => $this->uuid->getBytes() + 'parent_uuid' => $quotedUuid ]; } if ($this->field) { + $quotedParentUuid = Db\DbUtil::quoteBinaryCompat($this->parentUuid->getBytes(), $this->db->getDbAdapter()); $values = array_merge( [ - 'uuid' => $this->uuid->getBytes(), - 'parent_uuid' => $this->parentUuid->getBytes() + 'uuid' => $quotedUuid, + 'parent_uuid' => $quotedParentUuid ], $values ); } else { $values = array_merge( - ['uuid' => $this->uuid->getBytes()], + ['uuid' => $quotedUuid], $values ); } @@ -421,8 +424,8 @@ class CustomVariableForm extends CompatForm if (! empty($datalist)) { $this->db->insert('director_property_datalist', [ - 'property_uuid' => $this->uuid->getBytes(), - 'list_uuid' => $datalist['uuid'], + 'property_uuid' => $quotedUuid, + 'list_uuid' => Db\DbUtil::quoteBinaryCompat($datalist['uuid'], $this->db->getDbAdapter()), ]); } } @@ -460,27 +463,36 @@ class CustomVariableForm extends CompatForm ) { $this->db->delete( 'director_property', - Filter::matchAll(Filter::where('parent_uuid', $this->uuid->getBytes())) + Filter::matchAll(Filter::where( + 'parent_uuid', + Db\DbUtil::quoteBinaryCompat($this->uuid->getBytes(), $this->db->getDbAdapter()) + )) ); $this->db->delete( 'director_property_datalist', - Filter::matchAll(Filter::where('property_uuid', $this->uuid->getBytes())) + Filter::matchAll(Filter::where( + 'property_uuid', + Db\DbUtil::quoteBinaryCompat($this->uuid->getBytes(), $this->db->getDbAdapter()) + )) ); } if ($itemType && ($valueType === 'dynamic-array' || str_starts_with($valueType, 'datalist-'))) { $this->db->insert('director_property', [ - 'uuid' => Uuid::uuid4()->getBytes(), + 'uuid' => Db\DbUtil::quoteBinaryCompat(Uuid::uuid4()->getBytes(), $this->db->getDbAdapter()), 'key_name' => '0', 'value_type' => $itemType, - 'parent_uuid' => $this->uuid->getBytes() + 'parent_uuid' => Db\DbUtil::quoteBinaryCompat($this->uuid->getBytes(), $this->db->getDbAdapter()), ]); if (str_starts_with($valueType, 'datalist-')) { $this->db->insert('director_property_datalist', [ - 'property_uuid' => $this->uuid->getBytes(), - 'list_uuid' => $datalist['uuid'], + 'property_uuid' => Db\DbUtil::quoteBinaryCompat( + $this->uuid->getBytes(), + $this->db->getDbAdapter() + ), + 'list_uuid' => Db\DbUtil::quoteBinaryCompat($datalist['uuid'], $this->db->getDbAdapter()), ]); } } @@ -488,7 +500,7 @@ class CustomVariableForm extends CompatForm $storedKeyName = $this->db->fetchOne( $this->db->select() ->from('director_property', ['key_name']) - ->where('uuid', $this->uuid->getBytes()) + ->where('uuid', Db\DbUtil::quoteBinaryCompat($this->uuid->getBytes(), $this->db->getDbAdapter())) ); if ($storedKeyName !== $values['key_name']) { @@ -499,7 +511,7 @@ class CustomVariableForm extends CompatForm $this->db->update( 'director_property', $values, - Filter::where('uuid', $this->uuid->getBytes()) + Filter::where('uuid', Db\DbUtil::quoteBinaryCompat($this->uuid->getBytes(), $this->db->getDbAdapter())) ); } @@ -540,7 +552,7 @@ class CustomVariableForm extends CompatForm 'varvalue', 'property_uuid' ]) - ->where('property_uuid = ?', $rootUuid->getBytes()), + ->where('property_uuid = ?', Db\DbUtil::quoteBinaryCompat($rootUuid->getBytes(), $db)), [], PDO::FETCH_ASSOC ); @@ -551,7 +563,7 @@ class CustomVariableForm extends CompatForm "icinga_{$objectType}_var", ['varname' => $keyName], Filter::matchAll( - Filter::where('property_uuid', $rootUuid->getBytes()), + Filter::where('property_uuid', Db\DbUtil::quoteBinaryCompat($rootUuid->getBytes(), $db)), Filter::where("{$objectType}_id", $objectCustomVar["{$objectType}_id"]) ) ); @@ -585,7 +597,7 @@ class CustomVariableForm extends CompatForm "icinga_{$objectType}_var", ['varvalue' => json_encode($varValue)], Filter::matchAll( - Filter::where('property_uuid', $rootUuid->getBytes()), + Filter::where('property_uuid', Db\DbUtil::quoteBinaryCompat($rootUuid->getBytes(), $db)), Filter::where("{$objectType}_id", $objectCustomVar["{$objectType}_id"]) ) ); diff --git a/application/forms/CustomVariablesForm.php b/application/forms/CustomVariablesForm.php index 7cca88c4..82c29772 100644 --- a/application/forms/CustomVariablesForm.php +++ b/application/forms/CustomVariablesForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Director\Forms; use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry; +use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\Forms\DictionaryElements\Dictionary; use Icinga\Module\Director\Objects\DirectorActivityLog; use Icinga\Module\Director\Objects\IcingaHost; @@ -215,6 +216,7 @@ class CustomVariablesForm extends CompatForm $values = $propertiesElement->getDictionary(); $itemsToRemove = $propertiesElement->getItemsToRemove(); $type = $this->object->getShortTableName(); + $db = $this->object->getDb(); foreach ($this->objectProperties as $key => $property) { $propertyUuid = Uuid::fromBytes($property['uuid']); if (isset($property['removed'])) { @@ -243,8 +245,8 @@ class CustomVariablesForm extends CompatForm $this->object->getConnection()->insert( "icinga_$type" . '_property', [ - $type . '_uuid' => $this->object->uuid, - 'property_uuid' => $propertyUuid->getBytes() + $type . '_uuid' => DbUtil::quoteBinaryCompat($this->object->uuid, $db), + 'property_uuid' => DbUtil::quoteBinaryCompat($propertyUuid->getBytes(), $db) ] ); } @@ -273,7 +275,7 @@ class CustomVariablesForm extends CompatForm $db ->select() ->from('icinga_' . $type . '_var') - ->where('property_uuid IN (?)', $itemsToRemoveUuids) + ->where('property_uuid IN (?)', DbUtil::quoteBinaryCompat($itemsToRemoveUuids, $db)) ); foreach ($propertyAsObjectVar as $propertyAsObjectVarRow) { diff --git a/application/forms/DictionaryElements/Dictionary.php b/application/forms/DictionaryElements/Dictionary.php index 1e9d8f75..f49e10f1 100644 --- a/application/forms/DictionaryElements/Dictionary.php +++ b/application/forms/DictionaryElements/Dictionary.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Forms\DictionaryElements; +use Icinga\Module\Director\Db\DbUtil; use Icinga\Web\Session; use ipl\Html\FormElement\FieldsetElement; use ipl\Web\Widget\EmptyStateBar; @@ -104,6 +105,11 @@ class Dictionary extends FieldsetElement $this->addElement('hidden', 'items_added', ['value' => implode(', ', $addedItems)]); $count = 0; foreach ($this->items as $item) { + $item['uuid'] = DbUtil::binaryResult($item['uuid']); + if (isset($item['parent_uuid'])) { + $item['parent_uuid'] = DbUtil::binaryResult($item['parent_uuid']); + } + $element = new DictionaryItem($count, $item); // Only allow removal of items if the dictionary allows it and the item allows it diff --git a/application/forms/DictionaryElements/DictionaryItem.php b/application/forms/DictionaryElements/DictionaryItem.php index 79ede647..977b9e60 100644 --- a/application/forms/DictionaryElements/DictionaryItem.php +++ b/application/forms/DictionaryElements/DictionaryItem.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Forms\DictionaryElements; use Icinga\Application\Config; use Icinga\Module\Director\Db; +use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\Forms\Validator\DatalistEntryValidator; use Icinga\Module\Director\Web\Form\Element\ArrayElement; use Icinga\Module\Director\Web\Form\Element\IplBoolean; @@ -48,7 +49,8 @@ class DictionaryItem extends FieldsetElement ['dp' => 'director_property'], ['value_type' => 'dp.value_type'] ) - ->where('dp.parent_uuid = ?', $uuid->getBytes()); + ->where('dp.parent_uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); + return $db->fetchOne($query); } @@ -69,7 +71,7 @@ class DictionaryItem extends FieldsetElement ) ->join(['dl' => 'director_datalist'], 'dl.id = dle.list_id', []) ->join(['dpl' => 'director_property_datalist'], 'dl.uuid = dpl.list_uuid', []) - ->where('dpl.property_uuid = ?', $uuid->getBytes()); + ->where('dpl.property_uuid = ?', Db\DbUtil::quoteBinaryCompat($uuid->getBytes(), $db)); return $db->fetchPairs($query); } @@ -78,7 +80,7 @@ class DictionaryItem extends FieldsetElement { $this->addElement('hidden', 'name', ['value' => $this->fields['key_name'] ?? '']); $this->addElement('hidden', 'type', ['value' => $this->fields['value_type'] ?? '']); - $this->addElement('hidden', 'label', ['value' => $this->fields['key_name'] ?? '']); + $this->addElement('hidden', 'label', ['value' => $this->fields['label'] ?? '']); $this->addElement('hidden', 'parent_type', ['value' => $this->fields['parent_type'] ?? '']); $this->addElement('hidden', 'inherited'); @@ -280,6 +282,8 @@ class DictionaryItem extends FieldsetElement 'parent_type' => $property['parent_type'] ?? '' ]; + $property['uuid'] = Dbutil::binaryResult($property['uuid'] ?? ''); + if ( $property['value_type'] === 'dynamic-array' || ( @@ -373,7 +377,7 @@ class DictionaryItem extends FieldsetElement 'children' => 'COUNT(cdp.uuid)' ] ) - ->where('dp.parent_uuid = ?', $parentUuid->getBytes()) + ->where('dp.parent_uuid = ?', Db\DbUtil::quoteBinaryCompat($parentUuid->getBytes(), $db)) ->joinLeft( ['cdp' => 'director_property'], 'cdp.parent_uuid = dp.uuid', @@ -384,6 +388,12 @@ class DictionaryItem extends FieldsetElement ->order('key_name'); $propertyItems = $db->fetchAll($query, fetchMode: PDO::FETCH_ASSOC); + foreach ($propertyItems as $key => $propertyItem) { + $propertyItem['uuid'] = DbUtil::binaryResult($propertyItem['uuid']); + $propertyItem['parent_uuid'] = DbUtil::binaryResult($propertyItem['parent_uuid']); + $propertyItems[$key] = $propertyItem; + } + if (empty($values)) { return $propertyItems; } diff --git a/application/forms/ObjectCustomvarForm.php b/application/forms/ObjectCustomvarForm.php index 8dbc6c42..2d79a379 100644 --- a/application/forms/ObjectCustomvarForm.php +++ b/application/forms/ObjectCustomvarForm.php @@ -103,7 +103,8 @@ class ObjectCustomvarForm extends CompatForm ->getNamespace('director.variables')->get('added-properties', []); foreach ($properties as $property) { if (! isset($alreadyAddedProperties[$property->key_name])) { - $propUuidKeyPairs[Uuid::fromBytes($property->uuid)->toString()] = $property->key_name; + $uuid = DbUtil::binaryResult($property->uuid); + $propUuidKeyPairs[Uuid::fromBytes($uuid)->toString()] = $property->key_name; } } diff --git a/library/Director/CustomVariable/CustomVariable.php b/library/Director/CustomVariable/CustomVariable.php index d37afbf3..17acf55c 100644 --- a/library/Director/CustomVariable/CustomVariable.php +++ b/library/Director/CustomVariable/CustomVariable.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Director\CustomVariable; use Exception; use Icinga\Module\Director\Db\Cache\PrefetchCache; +use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer; use InvalidArgumentException; @@ -309,7 +310,7 @@ abstract class CustomVariable implements IcingaConfigRenderer } if (property_exists($row, 'property_uuid') && $row->property_uuid) { - $var->setUuid(Uuid::fromBytes($row->property_uuid)); + $var->setUuid(Uuid::fromBytes(DbUtil::binaryResult($row->property_uuid))); } $var->loadedFromDb = true; diff --git a/library/Director/CustomVariable/CustomVariables.php b/library/Director/CustomVariable/CustomVariables.php index f7af9822..56a5b439 100644 --- a/library/Director/CustomVariable/CustomVariables.php +++ b/library/Director/CustomVariable/CustomVariables.php @@ -246,7 +246,7 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer ]; if ($uuid) { - $row['property_uuid'] = $uuid; + $row['property_uuid'] = Db\DbUtil::quoteBinaryCompat($uuid, $db); } $db->insert($table, $row); @@ -267,7 +267,7 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer ]; if ($object->getShortTableName() === 'host' && $uuid) { - $data['property_uuid'] = $uuid; + $data['property_uuid'] = Db\DbUtil::quoteBinaryCompat($uuid, $db); } $db->update( diff --git a/library/Director/RestApi/IcingaObjectHandler.php b/library/Director/RestApi/IcingaObjectHandler.php index 040242b4..41acd805 100644 --- a/library/Director/RestApi/IcingaObjectHandler.php +++ b/library/Director/RestApi/IcingaObjectHandler.php @@ -9,6 +9,7 @@ use Icinga\Exception\ProgrammingError; use Icinga\Module\Director\Core\CoreApi; use Icinga\Module\Director\CustomVariable\CustomVariables; use Icinga\Module\Director\Data\Exporter; +use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\DirectorObject\Lookup\ServiceFinder; use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\Objects\IcingaHost; @@ -253,7 +254,7 @@ class IcingaObjectHandler extends RequestHandler $objectPropertyWhere = $db->getDbAdapter()->quoteInto( "{$type}_uuid = ?", - Uuid::fromBytes($this->object->get('uuid'))->getBytes() + Uuid::fromBytes(DbUtil::binaryResult($this->object->get('uuid')))->getBytes() ); $db->getDbAdapter()->delete( 'icinga_' . $type . '_property', @@ -312,7 +313,7 @@ class IcingaObjectHandler extends RequestHandler 'icinga_' . $type . '_property', [ 'property_uuid' => $customPropertyUuid, - $type . '_uuid' => $object->get('uuid') + $type . '_uuid' => DbUtil::quoteBinaryCompat($object->get('uuid'), $db->getDbAdapter()) ] ); diff --git a/library/Director/Web/Controller/ObjectController.php b/library/Director/Web/Controller/ObjectController.php index 6d1688dd..d476805c 100644 --- a/library/Director/Web/Controller/ObjectController.php +++ b/library/Director/Web/Controller/ObjectController.php @@ -8,10 +8,12 @@ use Icinga\Exception\InvalidPropertyException; use Icinga\Exception\NotFoundError; use Icinga\Exception\ProgrammingError; use Icinga\Module\Director\Dashboard\Dashlet\DeploymentDashlet; +use Icinga\Module\Director\Data\Db\DbConnection; use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry; use Icinga\Module\Director\Db\Branch\Branch; use Icinga\Module\Director\Db\Branch\BranchedObject; use Icinga\Module\Director\Db\Branch\UuidLookup; +use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\Deployment\DeploymentInfo; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; use Icinga\Module\Director\Exception\NestingError; @@ -652,7 +654,7 @@ abstract class ObjectController extends ActionController 'label' => 'dp.label', 'value_type' => 'dp.value_type' ] - )->where("parent_uuid = ?", $dictionaryUuid); + )->where("parent_uuid = ?", DbUtil::quoteBinaryCompat($dictionaryUuid, $db->getDbAdapter())); return $db->getDbAdapter()->fetchAll($query, fetchMode: PDO::FETCH_ASSOC); } @@ -670,6 +672,19 @@ abstract class ObjectController extends ActionController return $db->getDbAdapter()->fetchRow($query); } + private function valueTypeOrderExpr(DbConnection $db, array $types): string + { + if ($db->isPgsql()) { + $cases = []; + foreach ($types as $i => $type) { + $cases[] = "WHEN '$type' THEN " . ($i + 1); + } + return 'CASE dp.value_type ' . implode(' ', $cases) . ' ELSE ' . (count($types) + 1) . ' END'; + } + + return "FIELD(dp.value_type, '" . implode("', '", $types) . "')"; + } + /** * Get custom properties for the host. * @@ -687,11 +702,14 @@ abstract class ObjectController extends ActionController $uuids = []; $db = $this->db(); foreach ($parents as $parent) { - $uuids[] = IcingaObject::loadByType($type, $parent, $db)->get('uuid'); + $uuids[] = DbUtil::quoteBinaryCompat( + IcingaObject::loadByType($type, $parent, $db)->get('uuid'), + $db->getDbAdapter() + ); } $objectUuid = $object->get('uuid'); - $uuids[] = $object->get('uuid'); + $uuids[] = Dbutil::quoteBinaryCompat($objectUuid, $db->getDbAdapter()); $query = $db->getDbAdapter() ->select() ->from( @@ -716,11 +734,17 @@ abstract class ObjectController extends ActionController [] ) ->where('iop.' . $type . '_uuid IN (?)', $uuids) - ->group(['dp.uuid', 'dp.key_name', 'dp.value_type', 'dp.label']) - ->order( - "FIELD(dp.value_type, 'string', 'number', 'bool', 'datalist-strict', 'datalist-non-strict'," - . " 'dynamic-array', 'fixed-dictionary', 'dynamic-dictionary')" - ) + ->group(['dp.uuid', 'dp.key_name', 'dp.value_type', 'dp.label', $type . '_uuid']) + ->order($this->valueTypeOrderExpr($db, [ + 'string', + 'number', + 'bool', + 'datalist-strict', + 'datalist-non-strict', + 'dynamic-array', + 'fixed-dictionary', + 'dynamic-dictionary' + ])) ->order('children') ->order('key_name'); @@ -739,6 +763,8 @@ abstract class ObjectController extends ActionController } foreach ($db->getDbAdapter()->fetchAll($query, fetchMode: PDO::FETCH_ASSOC) as $row) { + $row['uuid'] = DbUtil::binaryResult($row['uuid']); + $row[$type . '_uuid'] = DbUtil::binaryResult($row[$type . '_uuid']); if ($objectUuid === $row[$type . '_uuid']) { $row['allow_removal'] = true; } else { @@ -775,18 +801,19 @@ abstract class ObjectController extends ActionController 'cdp.parent_uuid = dp.uuid', [] ) - ->where('dp.' . 'uuid IN (?)', $addedProperties) + ->where('dp.' . 'uuid IN (?)', DbUtil::quoteBinaryCompat($addedProperties, $db->getDbAdapter())) ->group(['dp.uuid', 'dp.key_name', 'dp.value_type', 'dp.label']) - ->order( - "FIELD(dp.value_type, 'string', 'number', 'bool', 'datalist-strict', 'datalist-non-strict'," - . " 'dynamic-array', 'fixed-array', 'fixed-dictionary', 'dynamic-dictionary')" - ) + ->order($this->valueTypeOrderExpr($db, [ + 'string', 'number', 'bool', 'datalist-strict', 'datalist-non-strict', + 'dynamic-array', 'fixed-array', 'fixed-dictionary', 'dynamic-dictionary' + ])) ->order('children') ->order('key_name'); foreach ($db->getDbAdapter()->fetchAll($query, fetchMode: PDO::FETCH_ASSOC) as $row) { $row['allow_removal'] = true; - $row['host_uuid'] = $this->object->get('uuid'); + $row['host_uuid'] = DbUtil::binaryResult($this->object->get('uuid')); + $row['uuid'] = DbUtil::binaryResult($row['uuid']); if (! isset($result[$row['key_name']])) { $row['new'] = true; } diff --git a/library/Director/Web/Widget/CustomVarFieldsTable.php b/library/Director/Web/Widget/CustomVarFieldsTable.php index df8f45c6..1f6e1021 100644 --- a/library/Director/Web/Widget/CustomVarFieldsTable.php +++ b/library/Director/Web/Widget/CustomVarFieldsTable.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Web\Widget; +use Icinga\Module\Director\Db\DbUtil; use ipl\Html\HtmlElement; use ipl\Html\Table; use ipl\I18n\Translation; @@ -27,13 +28,15 @@ class CustomVarFieldsTable extends Table protected function assemble() { foreach ($this->properties as $property) { + $propertyUuid = DbUtil::binaryResult($property->uuid); $url = Url::fromPath( 'director/customvar', - ['uuid' => Uuid::fromBytes($property->uuid)->toString()] + ['uuid' => Uuid::fromBytes($propertyUuid)->toString()] ); if (isset($property->parent_uuid)) { - $url->addParams(['parent_uuid' => Uuid::fromBytes($property->parent_uuid)->toString()]); + $parentUuid = DbUtil::binaryResult($property->parent_uuid); + $url->addParams(['parent_uuid' => Uuid::fromBytes($parentUuid)->toString()]); } $columns = [ diff --git a/schema/pgsql-migrations/upgrade_192.sql b/schema/pgsql-migrations/upgrade_192.sql new file mode 100644 index 00000000..1122bed3 --- /dev/null +++ b/schema/pgsql-migrations/upgrade_192.sql @@ -0,0 +1,180 @@ +CREATE TYPE enum_property_value_type AS ENUM( + 'string', + 'number', + 'bool', + 'fixed-array', + 'dynamic-array', + 'fixed-dictionary', + 'dynamic-dictionary', + 'datalist-strict', + 'datalist-non-strict' +); + +CREATE TABLE director_property ( + uuid bytea CHECK(LENGTH(uuid) = 16) NOT NULL, + parent_uuid bytea CHECK(LENGTH(parent_uuid) = 16) DEFAULT NULL, + key_name character varying(255) NOT NULL, + label character varying(255) DEFAULT NULL, + description text DEFAULT NULL, + value_type enum_property_value_type NOT NULL, + category_id integer DEFAULT NULL, + PRIMARY KEY (uuid), + CONSTRAINT director_property_category + FOREIGN KEY (category_id) + REFERENCES director_datafield_category (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +-- Unique key_name at root level (no parent) +CREATE UNIQUE INDEX unique_property_name_root + ON director_property (key_name) + WHERE parent_uuid IS NULL; + +-- Unique (key_name, parent_uuid) for nested properties +CREATE UNIQUE INDEX unique_property_name_parent + ON director_property (key_name, parent_uuid) + WHERE parent_uuid IS NOT NULL; + +CREATE TABLE icinga_host_property ( + host_uuid bytea CHECK(LENGTH(host_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (host_uuid, property_uuid), + CONSTRAINT icinga_host_property_host + FOREIGN KEY (host_uuid) + REFERENCES icinga_host (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_host_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE TABLE icinga_service_property ( + service_uuid bytea CHECK(LENGTH(service_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (service_uuid, property_uuid), + CONSTRAINT icinga_service_property_service + FOREIGN KEY (service_uuid) + REFERENCES icinga_service (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_service_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE TABLE icinga_command_property ( + command_uuid bytea CHECK(LENGTH(command_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (command_uuid, property_uuid), + CONSTRAINT icinga_command_property_command + FOREIGN KEY (command_uuid) + REFERENCES icinga_command (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_command_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE TABLE icinga_notification_property ( + notification_uuid bytea CHECK(LENGTH(notification_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (notification_uuid, property_uuid), + CONSTRAINT icinga_notification_property_notification + FOREIGN KEY (notification_uuid) + REFERENCES icinga_notification (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_notification_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE TABLE icinga_service_set_property ( + service_set_uuid bytea CHECK(LENGTH(service_set_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (service_set_uuid, property_uuid), + CONSTRAINT icinga_service_set_property_service_set + FOREIGN KEY (service_set_uuid) + REFERENCES icinga_service_set (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_service_set_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE TABLE icinga_user_property ( + user_uuid bytea CHECK(LENGTH(user_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (user_uuid, property_uuid), + CONSTRAINT icinga_user_property_user + FOREIGN KEY (user_uuid) + REFERENCES icinga_user (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_user_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE UNIQUE INDEX IF NOT EXISTS director_datalist_uuid_unique + ON director_datalist (uuid); + +CREATE TABLE director_property_datalist ( + list_uuid bytea CHECK(LENGTH(list_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + PRIMARY KEY (list_uuid, property_uuid), + CONSTRAINT director_list_property_list + FOREIGN KEY (list_uuid) + REFERENCES director_datalist (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT director_property_list_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +ALTER TABLE icinga_host_var + ADD COLUMN property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL; + +ALTER TABLE icinga_service_var + ADD COLUMN property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL; + +ALTER TABLE icinga_command_var + ADD COLUMN property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL; + +ALTER TABLE icinga_notification_var + ADD COLUMN property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL; + +ALTER TABLE icinga_service_set_var + ADD COLUMN property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL; + +ALTER TABLE icinga_user_var + ADD COLUMN property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL; + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (192, NOW()); diff --git a/schema/pgsql.sql b/schema/pgsql.sql index 6647699b..64894798 100644 --- a/schema/pgsql.sql +++ b/schema/pgsql.sql @@ -17,6 +17,18 @@ CREATE TYPE enum_merge_behaviour AS ENUM('set', 'add', 'substract', 'override'); CREATE TYPE enum_set_merge_behaviour AS ENUM('override', 'extend', 'blacklist'); CREATE TYPE enum_command_object_type AS ENUM('object', 'template', 'external_object'); CREATE TYPE enum_apply_object_type AS ENUM('object', 'template', 'apply', 'external_object'); +CREATE TYPE enum_property_value_type AS ENUM( + 'string', + 'number', + 'bool', + 'fixed-array', + 'dynamic-array', + 'fixed-dictionary', + 'dynamic-dictionary', + 'datalist-strict', + 'datalist-non-strict' +); + CREATE TYPE enum_state_name AS ENUM('OK', 'Warning', 'Critical', 'Unknown', 'Up', 'Down'); CREATE TYPE enum_type_name AS ENUM('DowntimeStart', 'DowntimeEnd', 'DowntimeRemoved', 'Custom', 'Acknowledgement', 'Problem', 'Recovery', 'FlappingStart', 'FlappingEnd'); CREATE TYPE enum_sync_rule_object_type AS ENUM( @@ -246,7 +258,7 @@ CREATE INDEX start_time_idx ON director_deployment_log (start_time); CREATE TABLE director_datalist ( id serial, - uuid bytea CHECK(LENGTH(uuid) = 16) NOT NULL, + uuid bytea UNIQUE CHECK(LENGTH(uuid) = 16) NOT NULL, list_name character varying(255) NOT NULL, owner character varying(255) NOT NULL, PRIMARY KEY (id) @@ -319,6 +331,47 @@ CREATE TABLE director_datafield_setting ( CREATE INDEX director_datafield_datafield ON director_datafield_setting (datafield_id); +CREATE TABLE director_property ( + uuid bytea CHECK(LENGTH(uuid) = 16) NOT NULL, + parent_uuid bytea CHECK(LENGTH(parent_uuid) = 16) DEFAULT NULL, + key_name character varying(255) NOT NULL, + label character varying(255) DEFAULT NULL, + description text DEFAULT NULL, + value_type enum_property_value_type NOT NULL, + category_id integer DEFAULT NULL, + PRIMARY KEY (uuid), + CONSTRAINT director_property_category + FOREIGN KEY (category_id) + REFERENCES director_datafield_category (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE UNIQUE INDEX unique_property_name_root + ON director_property (key_name) + WHERE parent_uuid IS NULL; + +CREATE UNIQUE INDEX unique_property_name_parent + ON director_property (key_name, parent_uuid) + WHERE parent_uuid IS NOT NULL; + +CREATE TABLE director_property_datalist ( + list_uuid bytea CHECK(LENGTH(list_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + PRIMARY KEY (list_uuid, property_uuid), + CONSTRAINT director_list_property_list + FOREIGN KEY (list_uuid) + REFERENCES director_datalist (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT director_property_list_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + + CREATE TABLE director_schema_migration ( schema_version SMALLINT NOT NULL, migration_time TIMESTAMP WITH TIME ZONE NOT NULL, @@ -573,12 +626,31 @@ CREATE TABLE icinga_command_field ( ); +CREATE TABLE icinga_command_property ( + command_uuid bytea CHECK(LENGTH(command_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (command_uuid, property_uuid), + CONSTRAINT icinga_command_property_command + FOREIGN KEY (command_uuid) + REFERENCES icinga_command (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_command_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + + CREATE TABLE icinga_command_var ( command_id integer NOT NULL, checksum bytea DEFAULT NULL UNIQUE CHECK(LENGTH(checksum) = 20), varname character varying(255) NOT NULL, varvalue text DEFAULT NULL, format enum_property_format NOT NULL DEFAULT 'string', + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL, PRIMARY KEY (command_id, varname), CONSTRAINT icinga_command_var_command FOREIGN KEY (command_id) @@ -802,12 +874,30 @@ CREATE INDEX host_field_datafield ON icinga_host_field (datafield_id); COMMENT ON COLUMN icinga_host_field.host_id IS 'Makes only sense for templates'; +CREATE TABLE icinga_host_property ( + host_uuid bytea CHECK(LENGTH(host_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (host_uuid, property_uuid), + CONSTRAINT icinga_host_property_host + FOREIGN KEY (host_uuid) + REFERENCES icinga_host (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_host_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + CREATE TABLE icinga_host_var ( host_id integer NOT NULL, checksum bytea DEFAULT NULL UNIQUE CHECK(LENGTH(checksum) = 20), varname character varying(255) NOT NULL, varvalue text DEFAULT NULL, format enum_property_format, -- immer string vorerst + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL, PRIMARY KEY (host_id, varname), CONSTRAINT icinga_host_var_host FOREIGN KEY (host_id) @@ -975,12 +1065,31 @@ CREATE INDEX service_inheritance_service ON icinga_service_inheritance (service_ CREATE INDEX service_inheritance_service_parent ON icinga_service_inheritance (parent_service_id); +CREATE TABLE icinga_service_property ( + service_uuid bytea CHECK(LENGTH(service_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (service_uuid, property_uuid), + CONSTRAINT icinga_service_property_service + FOREIGN KEY (service_uuid) + REFERENCES icinga_service (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_service_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + + CREATE TABLE icinga_service_var ( service_id integer NOT NULL, checksum bytea DEFAULT NULL UNIQUE CHECK(LENGTH(checksum) = 20), varname character varying(255) NOT NULL, varvalue text DEFAULT NULL, format enum_property_format, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL, PRIMARY KEY (service_id, varname), CONSTRAINT icinga_service_var_service FOREIGN KEY (service_id) @@ -1088,12 +1197,31 @@ CREATE INDEX service_set_inheritance_set ON icinga_service_set_inheritance (serv CREATE INDEX service_set_inheritance_parent ON icinga_service_set_inheritance (parent_service_set_id); +CREATE TABLE icinga_service_set_property ( + service_set_uuid bytea CHECK(LENGTH(service_set_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (service_set_uuid, property_uuid), + CONSTRAINT icinga_service_set_property_service_set + FOREIGN KEY (service_set_uuid) + REFERENCES icinga_service_set (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_service_set_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + + CREATE TABLE icinga_service_set_var ( service_set_id integer NOT NULL, checksum bytea DEFAULT NULL UNIQUE CHECK(LENGTH(checksum) = 20), varname character varying(255) NOT NULL, varvalue text DEFAULT NULL, format enum_property_format NOT NULL DEFAULT 'string', + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL, PRIMARY KEY (service_set_id, varname), CONSTRAINT icinga_service_set_var_service_set FOREIGN KEY (service_set_id) @@ -1370,6 +1498,7 @@ CREATE TABLE icinga_user_var ( varname character varying(255) NOT NULL, varvalue text DEFAULT NULL, format enum_property_format NOT NULL DEFAULT 'string', + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL, PRIMARY KEY (user_id, varname), CONSTRAINT icinga_user_var_user FOREIGN KEY (user_id) @@ -1407,6 +1536,24 @@ CREATE INDEX user_field_datafield ON icinga_user_field (datafield_id); COMMENT ON COLUMN icinga_user_field.user_id IS 'Makes only sense for templates'; +CREATE TABLE icinga_user_property ( + user_uuid bytea CHECK(LENGTH(user_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (user_uuid, property_uuid), + CONSTRAINT icinga_user_property_user + FOREIGN KEY (user_uuid) + REFERENCES icinga_user (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_user_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + + CREATE TABLE icinga_usergroup ( id serial, uuid bytea UNIQUE CHECK(LENGTH(uuid) = 16), @@ -1822,6 +1969,7 @@ CREATE TABLE icinga_notification_var ( varname VARCHAR(255) NOT NULL, varvalue TEXT DEFAULT NULL, format enum_property_format, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) DEFAULT NULL, PRIMARY KEY (notification_id, varname), CONSTRAINT icinga_notification_var_notification FOREIGN KEY (notification_id) @@ -1858,6 +2006,24 @@ CREATE INDEX notification_field_datafield ON icinga_notification_field (datafiel COMMENT ON COLUMN icinga_notification_field.notification_id IS 'Makes only sense for templates'; +CREATE TABLE icinga_notification_property ( + notification_uuid bytea CHECK(LENGTH(notification_uuid) = 16) NOT NULL, + property_uuid bytea CHECK(LENGTH(property_uuid) = 16) NOT NULL, + required enum_boolean NOT NULL DEFAULT 'n', + PRIMARY KEY (notification_uuid, property_uuid), + CONSTRAINT icinga_notification_property_notification + FOREIGN KEY (notification_uuid) + REFERENCES icinga_notification (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_notification_custom_property + FOREIGN KEY (property_uuid) + REFERENCES director_property (uuid) + ON DELETE CASCADE + ON UPDATE CASCADE +); + + CREATE TABLE icinga_notification_inheritance ( notification_id integer NOT NULL, parent_notification_id integer NOT NULL, @@ -2783,4 +2949,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (190, NOW()); + VALUES (192, NOW());