<?php
/**
 * Demo Oracle — resolution de la source du SQL Monitor.
 *
 * Le module complet arbitre entre trois sources : GV$SQL_PLAN_MONITOR sous
 * Tuning Pack, l'activite ASH sous Diagnostic Pack, l'echantillonnage de
 * GV$SESSION sans licence. La demonstration ne porte QUE la troisieme — les
 * deux premieres lisent des vues sous licence, et le module s'interdit d'y
 * toucher.
 *
 * Ce n'est pas une amputation : c'est precisement ce qu'il y a a montrer. Un
 * moniteur d'execution qui fonctionne la ou Oracle facture un pack.
 *
 * Safe to include multiple times (guard sur class_exists).
 */
/*
 * Les dependances sont chargees AVANT la garde, et c'est indispensable.
 *
 * PHP lie les declarations de classe a la COMPILATION du fichier, donc avant
 * d'en executer la premiere instruction. Quand la garde s'evalue, la classe
 * definie plus bas existe deja : la garde `return` systematiquement, et tout
 * ce qui la suit est du code mort. Un require_once place apres elle ne
 * s'execute jamais.
 */
require_once __DIR__ . '/DemoOracleAshSource.php';

if (class_exists('DemoOracleSqlMonSource')) return;

class DemoOracleSqlMonSource
{
    /** GV$SESSION echantillonnee + workarea + longops. Aucune licence. */
    public const SRC_SESSION = 'session';

    /** Le plan du curseur, estime : ni starts ni output_rows. */
    public const PLAN_ESTIMATED = 'sql_plan';

    /** L'activite est echantillonnee par la page, un releve par appel. */
    public const ACT_SAMPLED = 'sampled';

    /**
     * V$SESSION.SQL_PLAN_LINE_ID existe-t-elle sur cette base ?
     *
     * C'est la colonne qui porte toute l'attribution ligne a ligne. Elle
     * n'existe qu'a partir de 11g, et rien n'assure qu'elle soit alimentee
     * quand CONTROL_MANAGEMENT_PACK_ACCESS vaut NONE. On ne le presume pas :
     * on sonde. WHERE ROWNUM = 0 fait echouer le PARSE sur ORA-00904 si la
     * colonne manque, sans rien lire ni couter.
     *
     * Une colonne presente mais jamais alimentee n'est pas detectee ici : ce
     * cas se voit a l'usage, quand aucun echantillon ne porte de ligne de
     * plan, et la page bascule alors d'elle-meme en activite globale.
     */
    public static function planLineColumnExists($conn): bool
    {
        $stm = @oci_parse($conn, "SELECT sql_plan_line_id FROM V\$SESSION WHERE ROWNUM = 0");
        if (!$stm) return false;

        $ok = (bool)@oci_execute($stm, OCI_NO_AUTO_COMMIT);
        oci_free_statement($stm);
        return $ok;
    }

    /**
     * La source, toujours la meme — mais l'etiquette change.
     *
     * `licensed` ne conditionne AUCUNE requete : il sert uniquement a dire au
     * visiteur que sa base est licenciee et que la version complete lirait
     * alors le vrai Real-Time SQL Monitoring. C'est le premier appelant reel
     * de DemoOracleAshSource::hasTuningPack().
     *
     * @return array{source:string, plan_source:string, activity:string,
     *               plan_line:bool, licensed:bool}
     */
    public static function resolve($conn, bool $isCloud = false): array
    {
        return [
            'source'      => self::SRC_SESSION,
            'plan_source' => self::PLAN_ESTIMATED,
            'activity'    => self::ACT_SAMPLED,
            'plan_line'   => self::planLineColumnExists($conn),
            'licensed'    => DemoOracleAshSource::hasTuningPack($conn, $isCloud),
        ];
    }
}
