From 7f944af41f3214014becde824608bf3d03d07e15 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 17 Dec 2024 10:41:11 +0100 Subject: [PATCH] Introduce singleton `Icinga\Module\Icingadb\Common\Backend` --- library/Icingadb/Common/Backend.php | 168 ++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 library/Icingadb/Common/Backend.php diff --git a/library/Icingadb/Common/Backend.php b/library/Icingadb/Common/Backend.php new file mode 100644 index 000000000..135732dfb --- /dev/null +++ b/library/Icingadb/Common/Backend.php @@ -0,0 +1,168 @@ +get('icingadb', 'resource') + )); + + $config->options = [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ]; + if ($config->db === 'mysql') { + $config->options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET SESSION SQL_MODE='STRICT_TRANS_TABLES" + . ",NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'"; + } + + self::$db = new Connection($config); + + $adapter = self::$db->getAdapter(); + if ($adapter instanceof Pgsql) { + $quoted = $adapter->quoteIdentifier('user'); + self::$db->getQueryBuilder() + ->on(QueryBuilder::ON_SELECT_ASSEMBLED, function (&$sql) use ($quoted) { + // user is a reserved key word in PostgreSQL, so we need to quote it. + // TODO(lippserd): This is pretty hacky, + // reconsider how to properly implement identifier quoting. + $sql = str_replace(' user ', sprintf(' %s ', $quoted), $sql); + $sql = str_replace(' user.', sprintf(' %s.', $quoted), $sql); + $sql = str_replace('(user.', sprintf('(%s.', $quoted), $sql); + }) + ->on(QueryBuilder::ON_ASSEMBLE_SELECT, function (Select $select) { + // For SELECT DISTINCT, all ORDER BY columns must appear in SELECT list. + if (! $select->getDistinct() || ! $select->hasOrderBy()) { + return; + } + + $candidates = []; + foreach ($select->getOrderBy() as list($columnOrAlias, $_)) { + if ($columnOrAlias instanceof Expression) { + // Expressions can be and include anything, + // also columns that aren't already part of the SELECT list, + // so we're not trying to guess anything here. + // Such expressions must be in the SELECT list if necessary and + // referenced manually with an alias in ORDER BY. + continue; + } + + $candidates[$columnOrAlias] = true; + } + + foreach ($select->getColumns() as $alias => $column) { + if (is_int($alias)) { + if ($column instanceof Expression) { + // This is the complement to the above consideration. + // If it is an unaliased expression, ignore it. + continue; + } + } else { + unset($candidates[$alias]); + } + + if (! $column instanceof Expression) { + unset($candidates[$column]); + } + } + + if (! empty($candidates)) { + $select->columns(array_keys($candidates)); + } + }); + } + } + + return self::$db; + } + + /** + * Get the schema version of the Icinga DB + * + * @return int + */ + public static function getDbSchemaVersion(): int + { + if (self::$schemaVersion === null) { + self::$schemaVersion = Schema::on(self::getDb()) + ->columns('version') + ->first() + ->version; + } + + return self::$schemaVersion; + } + + /** + * Set the connection to the Icinga Redis + * + * Usually not required, as the connection is created on demand. Useful for testing. + * + * @param IcingaRedis $redis + * + * @return void + */ + public static function setRedis(IcingaRedis $redis): void + { + self::$redis = $redis; + } + + /** + * Get the connection to the Icinga Redis + * + * @return IcingaRedis + */ + public static function getRedis(): IcingaRedis + { + if (self::$redis === null) { + self::$redis = new IcingaRedis(); + } + + return self::$redis; + } +}