spip_nursit/ecrire/req/mysql.php
2023-06-01 17:30:12 +02:00

1794 lines
52 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/* *************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2019 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
/**
* Ce fichier contient les fonctions gérant
* les instructions SQL pour MySQL
*
* Ces instructions utilisent la librairie PHP Mysqli
*
* @package SPIP\Core\SQL\MySQL
*/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
if (!defined('_MYSQL_NOPLANES')) {
define('_MYSQL_NOPLANES', true);
}
/**
* Crée la première connexion à un serveur MySQL via MySQLi
*
* @param string $host Chemin du serveur
* @param int $port Port de connexion
* @param string $login Nom d'utilisateur
* @param string $pass Mot de passe
* @param string $db Nom de la base
* @param string $prefixe Préfixe des tables SPIP
* @return array|bool
* - false si la connexion a échoué
* - tableau décrivant la connexion sinon
*/
function req_mysql_dist($host, $port, $login, $pass, $db = '', $prefixe = '') {
if (!charger_php_extension('mysqli')) {
return false;
}
// si port est fourni mais pas host, c'est un socket -> compat avec vieille syntaxe de mysql_connect() et anciens fichiers connect.php
if (
$port and !is_numeric($socket = $port)
and (!$host or $host=='localhost')) {
$link = @mysqli_connect($host, $login, $pass, '', null, $socket);
}
elseif ($port) {
$link = @mysqli_connect($host, $login, $pass, '', $port);
}
else {
$link = @mysqli_connect($host, $login, $pass);
}
if (!$link) {
spip_log('Echec mysqli_connect. Erreur : ' . mysqli_connect_error(), 'mysql.' . _LOG_HS);
return false;
}
$last = '';
if (!$db) {
$ok = $link;
$db = 'spip';
} else {
$ok = mysqli_select_db($link, $db);
if (defined('_MYSQL_SET_SQL_MODE')
or defined('_MYSQL_SQL_MODE_TEXT_NOT_NULL') // compatibilite
) {
mysqli_query($link, $last = "set sql_mode=''");
}
}
spip_log("Connexion MySQLi vers $host, base $db, prefixe $prefixe " . ($ok ? "operationnelle" : 'impossible'),
_LOG_DEBUG);
return !$ok ? false : array(
'db' => $db,
'last' => $last,
'prefixe' => $prefixe ? $prefixe : $db,
'link' => $link,
'total_requetes' => 0,
);
}
$GLOBALS['spip_mysql_functions_1'] = array(
'alter' => 'spip_mysql_alter',
'count' => 'spip_mysql_count',
'countsel' => 'spip_mysql_countsel',
'create' => 'spip_mysql_create',
'create_base' => 'spip_mysql_create_base',
'create_view' => 'spip_mysql_create_view',
'date_proche' => 'spip_mysql_date_proche',
'delete' => 'spip_mysql_delete',
'drop_table' => 'spip_mysql_drop_table',
'drop_view' => 'spip_mysql_drop_view',
'errno' => 'spip_mysql_errno',
'error' => 'spip_mysql_error',
'explain' => 'spip_mysql_explain',
'fetch' => 'spip_mysql_fetch',
'seek' => 'spip_mysql_seek',
'free' => 'spip_mysql_free',
'hex' => 'spip_mysql_hex',
'in' => 'spip_mysql_in',
'insert' => 'spip_mysql_insert',
'insertq' => 'spip_mysql_insertq',
'insertq_multi' => 'spip_mysql_insertq_multi',
'listdbs' => 'spip_mysql_listdbs',
'multi' => 'spip_mysql_multi',
'optimize' => 'spip_mysql_optimize',
'query' => 'spip_mysql_query',
'quote' => 'spip_mysql_quote',
'replace' => 'spip_mysql_replace',
'replace_multi' => 'spip_mysql_replace_multi',
'repair' => 'spip_mysql_repair',
'select' => 'spip_mysql_select',
'selectdb' => 'spip_mysql_selectdb',
'set_charset' => 'spip_mysql_set_charset',
'get_charset' => 'spip_mysql_get_charset',
'showbase' => 'spip_mysql_showbase',
'showtable' => 'spip_mysql_showtable',
'update' => 'spip_mysql_update',
'updateq' => 'spip_mysql_updateq',
// association de chaque nom http d'un charset aux couples MySQL
'charsets' => array(
'cp1250' => array('charset' => 'cp1250', 'collation' => 'cp1250_general_ci'),
'cp1251' => array('charset' => 'cp1251', 'collation' => 'cp1251_general_ci'),
'cp1256' => array('charset' => 'cp1256', 'collation' => 'cp1256_general_ci'),
'iso-8859-1' => array('charset' => 'latin1', 'collation' => 'latin1_swedish_ci'),
//'iso-8859-6'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
'iso-8859-9' => array('charset' => 'latin5', 'collation' => 'latin5_turkish_ci'),
//'iso-8859-15'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
'utf-8' => array('charset' => 'utf8', 'collation' => 'utf8_general_ci')
)
);
/**
* Retrouver un link d'une connexion MySQL via MySQLi
*
* @param string $serveur Nom du serveur
* @return Object Information de connexion pour mysqli
*/
function _mysql_link($serveur = '') {
$link = &$GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
return $link;
}
/**
* Définit un charset pour la connexion avec Mysql
*
* @param string $charset Charset à appliquer
* @param string $serveur Nom de la connexion
* @param bool $requeter inutilisé
* @return resource Ressource de résultats pour fetch()
*/
function spip_mysql_set_charset($charset, $serveur = '', $requeter = true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
spip_log("changement de charset sql : " . "SET NAMES " . _q($charset), _LOG_DEBUG);
return mysqli_query($connexion['link'], $connexion['last'] = "SET NAMES " . _q($charset));
}
/**
* Teste si le charset indiqué est disponible sur le serveur SQL
*
* @param array|string $charset Nom du charset à tester.
* @param string $serveur Nom de la connexion
* @param bool $requeter inutilisé
* @return array Description du charset (son nom est dans 'charset')
*/
function spip_mysql_get_charset($charset = array(), $serveur = '', $requeter = true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$connexion['last'] = $c = "SHOW CHARACTER SET"
. (!$charset ? '' : (" LIKE " . _q($charset['charset'])));
return spip_mysql_fetch(mysqli_query($connexion['link'], $c), null, $serveur);
}
/**
* Exécute une requête Mysql (obsolète, ne plus utiliser)
*
* @deprecated Utiliser sql_query() ou autres
*
* @param string $query Requête
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return Resource Ressource pour fetch()
**/
function spip_query_db($query, $serveur = '', $requeter = true) {
return spip_mysql_query($query, $serveur, $requeter);
}
/**
* Exécute une requête MySQL, munie d'une trace à la demande
*
* @param string $query Requête
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return array|resource|string|bool
* - string : Texte de la requête si on ne l'exécute pas
* - ressource|bool : Si requête exécutée
* - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
*/
function spip_mysql_query($query, $serveur = '', $requeter = true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
$query = _mysql_traite_query($query, $db, $prefixe);
// renvoyer la requete inerte si demandee
if (!$requeter) {
return $query;
}
if (isset($_GET['var_profile'])) {
include_spip('public/tracer');
$t = trace_query_start();
} else {
$t = 0;
}
$connexion['last'] = $query;
$connexion['total_requetes']++;
// ajouter un debug utile dans log/mysql-slow.log ?
$debug = '';
if (defined('_DEBUG_SLOW_QUERIES') and _DEBUG_SLOW_QUERIES) {
if (isset($GLOBALS['debug']['aucasou'])) {
list(, $id, , $infos) = $GLOBALS['debug']['aucasou'];
$debug .= "BOUCLE$id @ " . (isset($infos[0]) ? $infos[0] : '') . " | ";
}
$debug .= $_SERVER['REQUEST_URI'] . ' + ' . $GLOBALS['ip'];
$debug = ' /* ' . mysqli_real_escape_string($link, str_replace('*/', '@/', $debug)) . ' */';
}
$r = mysqli_query($link, $query . $debug);
//Eviter de propager le GoneAway sur les autres requetes d'un même processus PHP
if ($e = spip_mysql_errno($serveur)) { // Log d'un Gone Away
if ($e == 2006) { //Si Gone Away on relance une connexion vierge
//Fermer la connexion defaillante
mysqli_close($connexion['link']);
unset($GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]);
//Relancer une connexion vierge
spip_connect($serveur);
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$link = $connexion['link'];
//On retente au cas où
$r = mysqli_query($link, $query . $debug);
}
}
// Log de l'erreur eventuelle
if ($e = spip_mysql_errno($serveur)) {
$e .= spip_mysql_error($query, $serveur);
} // et du fautif
return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
}
/**
* Modifie une structure de table MySQL
*
* @param string $query Requête SQL (sans 'ALTER ')
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return array|bool|string
* - string : Texte de la requête si on ne l'exécute pas
* - bool : Si requête exécutée
* - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
*/
function spip_mysql_alter($query, $serveur = '', $requeter = true) {
// ici on supprime les ` entourant le nom de table pour permettre
// la transposition du prefixe, compte tenu que les plugins ont la mauvaise habitude
// d'utiliser ceux-ci, copie-colle de phpmyadmin
$query = preg_replace(",^TABLE\s*`([^`]*)`,i", "TABLE \\1", $query);
return spip_mysql_query("ALTER " . $query, $serveur, $requeter); # i.e. que PG se debrouille
}
/**
* Optimise une table MySQL
*
* @param string $table Nom de la table
* @param string $serveur Nom de la connexion
* @param bool $requeter inutilisé
* @return bool Toujours true
*/
function spip_mysql_optimize($table, $serveur = '', $requeter = true) {
spip_mysql_query("OPTIMIZE TABLE " . $table);
return true;
}
/**
* Retourne une explication de requête (Explain) MySQL
*
* @param string $query Texte de la requête
* @param string $serveur Nom de la connexion
* @param bool $requeter inutilisé
* @return array Tableau de l'explication
*/
function spip_mysql_explain($query, $serveur = '', $requeter = true) {
if (strpos(ltrim($query), 'SELECT') !== 0) {
return array();
}
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
$query = 'EXPLAIN ' . _mysql_traite_query($query, $db, $prefixe);
$r = mysqli_query($link, $query);
return spip_mysql_fetch($r, null, $serveur);
}
/**
* Exécute une requête de sélection avec MySQL
*
* Instance de sql_select (voir ses specs).
*
* @see sql_select()
* @note
* Les `\n` et `\t` sont utiles au debusqueur.
*
* @param string|array $select Champs sélectionnés
* @param string|array $from Tables sélectionnées
* @param string|array $where Contraintes
* @param string|array $groupby Regroupements
* @param string|array $orderby Tris
* @param string $limit Limites de résultats
* @param string|array $having Contraintes posts sélections
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return array|bool|resource|string
* - string : Texte de la requête si on ne l'exécute pas
* - ressource si requête exécutée, ressource pour fetch()
* - false si la requête exécutée a ratée
* - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
*/
function spip_mysql_select(
$select,
$from,
$where = '',
$groupby = '',
$orderby = '',
$limit = '',
$having = '',
$serveur = '',
$requeter = true
) {
$from = (!is_array($from) ? $from : spip_mysql_select_as($from));
$query =
calculer_mysql_expression('SELECT', $select, ', ')
. calculer_mysql_expression('FROM', $from, ', ')
. calculer_mysql_expression('WHERE', $where)
. calculer_mysql_expression('GROUP BY', $groupby, ',')
. calculer_mysql_expression('HAVING', $having)
. ($orderby ? ("\nORDER BY " . spip_mysql_order($orderby)) : '')
. ($limit ? "\nLIMIT $limit" : '');
// renvoyer la requete inerte si demandee
if ($requeter === false) {
return $query;
}
$r = spip_mysql_query($query, $serveur, $requeter);
return $r ? $r : $query;
}
/**
* Prépare une clause order by
*
* Regroupe en texte les éléments si un tableau est donné
*
* @note
* 0+x avec un champ x commencant par des chiffres est converti par MySQL
* en le nombre qui commence x. Pas portable malheureusement, on laisse pour le moment.
*
* @param string|array $orderby Texte du orderby à préparer
* @return string Texte du orderby préparé
*/
function spip_mysql_order($orderby) {
return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
}
/**
* Prépare une clause WHERE pour MySQL
*
* Retourne une chaîne avec les bonnes parenthèses pour la
* contrainte indiquée, au format donnée par le compilateur
*
* @param array|string $v
* Description des contraintes
* - string : Texte du where
* - sinon tableau : A et B peuvent être de type string ou array,
* OP et C sont de type string :
* - array(A) : A est le texte du where
* - array(OP, A) : contrainte OP( A )
* - array(OP, A, B) : contrainte (A OP B)
* - array(OP, A, B, C) : contrainte (A OP (B) : C)
* @return string
* Contrainte pour clause WHERE
*/
function calculer_mysql_where($v) {
if (!is_array($v)) {
return $v;
}
$op = array_shift($v);
if (!($n = count($v))) {
return $op;
} else {
$arg = calculer_mysql_where(array_shift($v));
if ($n == 1) {
return "$op($arg)";
} else {
$arg2 = calculer_mysql_where(array_shift($v));
if ($n == 2) {
return "($arg $op $arg2)";
} else {
return "($arg $op ($arg2) : $v[0])";
}
}
}
}
/**
* Calcule un expression pour une requête, en cumulant chaque élément
* avec l'opérateur de liaison ($join) indiqué
*
* Renvoie grosso modo "$expression join($join, $v)"
*
* @param string $expression Mot clé de l'expression, tel que "WHERE" ou "ORDER BY"
* @param array|string $v Données de l'expression
* @param string $join Si les données sont un tableau, elles seront groupées par cette jointure
* @return string Texte de l'expression, une partie donc, du texte la requête.
*/
function calculer_mysql_expression($expression, $v, $join = 'AND') {
if (empty($v)) {
return '';
}
$exp = "\n$expression ";
if (!is_array($v)) {
return $exp . $v;
} else {
if (strtoupper($join) === 'AND') {
return $exp . join("\n\t$join ", array_map('calculer_mysql_where', $v));
} else {
return $exp . join($join, $v);
}
}
}
/**
* Renvoie des `nom AS alias`
*
* @param array $args
* @return string Sélection de colonnes pour une clause SELECT
*/
function spip_mysql_select_as($args) {
$res = '';
foreach ($args as $k => $v) {
if (substr($k, -1) == '@') {
// c'est une jointure qui se refere au from precedent
// pas de virgule
$res .= ' ' . $v;
} else {
if (!is_numeric($k)) {
$p = strpos($v, " ");
if ($p) {
$v = substr($v, 0, $p) . " AS `$k`" . substr($v, $p);
} else {
$v .= " AS `$k`";
}
}
$res .= ', ' . $v;
}
}
return substr($res, 2);
}
/**
* Changer les noms des tables ($table_prefix)
*
* TODO: Quand tous les appels SQL seront abstraits on pourra l'améliorer
*/
define('_SQL_PREFIXE_TABLE_MYSQL', '/([,\s])spip_/S');
/**
* Prépare le texte d'une requête avant son exécution
*
* Change les préfixes de tables SPIP par ceux véritables
*
* @param string $query Requête à préparer
* @param string $db Nom de la base de donnée
* @param string $prefixe Préfixe de tables à appliquer
* @param bool $echappe_textes Pour ne pas essayer de re-echapper une chaine deja echappee qu'on traite en recursif
* @return string Requête préparée
*/
function _mysql_traite_query($query, $db = '', $prefixe = '', $echappe_textes = true) {
if ($GLOBALS['mysql_rappel_nom_base'] and $db) {
$pref = '`' . $db . '`.';
} else {
$pref = '';
}
if ($prefixe) {
$pref .= $prefixe . "_";
}
if (!preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
$suite = '';
} else {
$suite = strstr($query, $regs[0]);
$query = substr($query, 0, -strlen($suite));
// propager le prefixe en cas de requete imbriquee
// il faut alors echapper les chaine avant de le faire, pour ne pas risquer de
// modifier une requete qui est en fait juste du texte dans un champ
if (stripos($suite, 'SELECT') !== false) {
if ($echappe_textes) {
list($suite_echap, $textes) = query_echappe_textes($suite);
}
else {
$suite_echap = $suite;
}
if (preg_match('/^(.*?)([(]\s*SELECT\b.*)$/si', $suite_echap, $r)) {
$suite_echap = $r[1] . _mysql_traite_query($r[2], $db, $prefixe, false);
if ($echappe_textes) {
$suite = query_reinjecte_textes($suite_echap, $textes);
}
else {
$suite = $suite_echap;
}
}
}
}
$r = preg_replace(_SQL_PREFIXE_TABLE_MYSQL, '\1' . $pref, $query) . $suite;
// en option, remplacer les emoji (que mysql ne sait pas gérer) en &#128169;
if (defined('_MYSQL_NOPLANES') and _MYSQL_NOPLANES and lire_meta('charset_sql_connexion') == 'utf8') {
include_spip('inc/charsets');
$r = utf8_noplanes($r);
}
#spip_log("_mysql_traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG);
return $r;
}
/**
* Sélectionne une base de données
*
* @param string $db
* Nom de la base à utiliser
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Inutilisé
*
* @return bool
* - True cas de succès.
* - False en cas d'erreur.
**/
function spip_mysql_selectdb($db, $serveur = '', $requeter = true) {
$link = _mysql_link($serveur);
$ok = mysqli_select_db($link, $db);
if (!$ok) {
spip_log('Echec mysqli_selectdb. Erreur : ' . mysqli_error($link), 'mysql.' . _LOG_CRITIQUE);
}
return $ok;
}
/**
* Retourne les bases de données accessibles
*
* Retourne un tableau du nom de toutes les bases de données
* accessibles avec les permissions de l'utilisateur SQL
* de cette connexion.
*
* Attention on n'a pas toujours les droits !
*
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Inutilisé
* @return array
* Liste de noms de bases de données
**/
function spip_mysql_listdbs($serveur = '', $requeter = true) {
$dbs = array();
if ($res = spip_mysql_query("SHOW DATABASES", $serveur)) {
while ($row = mysqli_fetch_assoc($res)) {
$dbs[] = $row['Database'];
}
}
return $dbs;
}
/**
* Crée une table SQL
*
* Crée une table SQL nommee `$nom` à partir des 2 tableaux `$champs` et `$cles`
*
* @note Le nom des caches doit être inferieur à 64 caractères
*
* @param string $nom Nom de la table SQL
* @param array $champs Couples (champ => description SQL)
* @param array $cles Couples (type de clé => champ(s) de la clé)
* @param bool $autoinc True pour ajouter un auto-incrément sur la Primary Key
* @param bool $temporary True pour créer une table temporaire
* @param string $serveur Nom de la connexion
* @param bool $requeter inutilisé
* @return array|null|resource|string
* - null si champs ou cles n'est pas un tableau
* - true si la requête réussie, false sinon.
*/
function spip_mysql_create(
$nom,
$champs,
$cles,
$autoinc = false,
$temporary = false,
$serveur = '',
$requeter = true
) {
$query = '';
$keys = '';
$s = '';
$p = '';
// certains plugins declarent les tables (permet leur inclusion dans le dump)
// sans les renseigner (laisse le compilo recuperer la description)
if (!is_array($champs) || !is_array($cles)) {
return;
}
$res = spip_mysql_query("SELECT version() as v", $serveur);
if (($row = mysqli_fetch_array($res)) && (version_compare($row['v'], '5.0', '>='))) {
spip_mysql_query("SET sql_mode=''", $serveur);
}
foreach ($cles as $k => $v) {
$keys .= "$s\n\t\t$k ($v)";
if ($k == "PRIMARY KEY") {
$p = $v;
}
$s = ",";
}
$s = '';
$character_set = "";
if (@$GLOBALS['meta']['charset_sql_base']) {
$character_set .= " CHARACTER SET " . $GLOBALS['meta']['charset_sql_base'];
}
if (@$GLOBALS['meta']['charset_collation_sql_base']) {
$character_set .= " COLLATE " . $GLOBALS['meta']['charset_collation_sql_base'];
}
foreach ($champs as $k => $v) {
$v = _mysql_remplacements_definitions_table($v);
if (preg_match(',([a-z]*\s*(\(\s*[0-9]*\s*\))?(\s*binary)?),i', $v, $defs)) {
if (preg_match(',(char|text),i', $defs[1])
and !preg_match(',(binary|CHARACTER|COLLATE),i', $v)
) {
$v = $defs[1] . $character_set . ' ' . substr($v, strlen($defs[1]));
}
}
$query .= "$s\n\t\t$k $v"
. (($autoinc && ($p == $k) && preg_match(',\b(big|small|medium)?int\b,i', $v))
? " auto_increment"
: ''
);
$s = ",";
}
$temporary = $temporary ? 'TEMPORARY' : '';
$q = "CREATE $temporary TABLE IF NOT EXISTS $nom ($query" . ($keys ? ",$keys" : '') . ")"
. " ENGINE=MyISAM"
. ($character_set ? " DEFAULT $character_set" : "")
. "\n";
return spip_mysql_query($q, $serveur);
}
/**
* Adapte pour Mysql la déclaration SQL d'une colonne d'une table
*
* @param string|array $query
* Définition SQL d'un champ de table ou liste de déclarations
* @return string|array
* Définition SQL adaptée pour MySQL d'un champ de table
*/
function _mysql_remplacements_definitions_table($query) {
// quelques remplacements
$num = "(\s*\([0-9]*\))?";
$enum = "(\s*\([^\)]*\))?";
$remplace = array(
'/VARCHAR(\s*[^\s\(])/is' => 'VARCHAR(255)\\1',
'/^TIMESTAMP($| NULL DEFAULT NULL)/is' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
);
if (is_string($query)) {
$query = preg_replace(array_keys($remplace), $remplace, $query);
} elseif (is_array($query)) {
$keys = array_keys($remplace);
foreach ($query as $k => $q) {
$query[$k] = preg_replace($keys, $remplace, $q);
}
}
return $query;
}
/**
* Crée une base de données MySQL
*
* @param string $nom Nom de la base
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return bool true si la base est créee.
**/
function spip_mysql_create_base($nom, $serveur = '', $requeter = true) {
return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
}
/**
* Crée une vue SQL nommée `$nom`
*
* @param string $nom
* Nom de la vue à creer
* @param string $query_select
* Texte de la requête de sélection servant de base à la vue
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Effectuer la requete, sinon la retourner
* @return bool|string
* - true si la vue est créée
* - false si erreur ou si la vue existe déja
* - string texte de la requête si $requeter vaut false
*/
function spip_mysql_create_view($nom, $query_select, $serveur = '', $requeter = true) {
if (!$query_select) {
return false;
}
// vue deja presente
if (sql_showtable($nom, false, $serveur)) {
spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", _LOG_ERREUR);
return false;
}
$query = "CREATE VIEW $nom AS " . $query_select;
return spip_mysql_query($query, $serveur, $requeter);
}
/**
* Supprime une table SQL
*
* @param string $table Nom de la table SQL
* @param string $exist True pour ajouter un test d'existence avant de supprimer
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return bool|string
* - string Texte de la requête si demandé
* - true si la requête a réussie, false sinon
*/
function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
if ($exist) {
$exist = " IF EXISTS";
}
return spip_mysql_query("DROP TABLE$exist $table", $serveur, $requeter);
}
/**
* Supprime une vue SQL
*
* @param string $view Nom de la vue SQL
* @param string $exist True pour ajouter un test d'existence avant de supprimer
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return bool|string
* - string Texte de la requête si demandé
* - true si la requête a réussie, false sinon
*/
function spip_mysql_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
if ($exist) {
$exist = " IF EXISTS";
}
return spip_mysql_query("DROP VIEW$exist $view", $serveur, $requeter);
}
/**
* Retourne une ressource de la liste des tables de la base de données
*
* @param string $match
* Filtre sur tables à récupérer
* @param string $serveur
* Connecteur de la base
* @param bool $requeter
* true pour éxecuter la requête
* false pour retourner le texte de la requête.
* @return ressource
* Ressource à utiliser avec sql_fetch()
**/
function spip_mysql_showbase($match, $serveur = '', $requeter = true) {
return spip_mysql_query("SHOW TABLES LIKE " . _q($match), $serveur, $requeter);
}
/**
* Répare une table SQL
*
* Utilise `REPAIR TABLE ...` de MySQL
*
* @param string $table Nom de la table SQL
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return bool|string
* - string Texte de la requête si demandée,
* - true si la requête a réussie, false sinon
*/
function spip_mysql_repair($table, $serveur = '', $requeter = true) {
return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
}
define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
/**
* Obtient la description d'une table ou vue MySQL
*
* Récupère la définition d'une table ou d'une vue avec colonnes, indexes, etc.
* au même format que la définition des tables SPIP, c'est à dire
* un tableau avec les clés
*
* - `field` (tableau colonne => description SQL) et
* - `key` (tableau type de clé => colonnes)
*
* @param string $nom_table Nom de la table SQL
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return array|string
* - chaîne vide si pas de description obtenue
* - string Texte de la requête si demandé
* - array description de la table sinon
*/
function spip_mysql_showtable($nom_table, $serveur = '', $requeter = true) {
$s = spip_mysql_query("SHOW CREATE TABLE `$nom_table`", $serveur, $requeter);
if (!$s) {
return '';
}
if (!$requeter) {
return $s;
}
list(, $a) = mysqli_fetch_array($s, MYSQLI_NUM);
if (preg_match(_MYSQL_RE_SHOW_TABLE, $a, $r)) {
$desc = $r[1];
// extraction d'une KEY éventuelle en prenant garde de ne pas
// relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
if (preg_match("/^(.*?),([^,]*\sKEY[ (].*)$/s", $desc, $r)) {
$namedkeys = $r[2];
$desc = $r[1];
} else {
$namedkeys = "";
}
$fields = array();
foreach (preg_split("/,\s*`/", $desc) as $v) {
preg_match("/^\s*`?([^`]*)`\s*(.*)/", $v, $r);
$fields[strtolower($r[1])] = $r[2];
}
$keys = array();
foreach (preg_split('/\)\s*(,|$)/', $namedkeys) as $v) {
if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/", $v, $r)) {
$k = str_replace("`", '', trim($r[1]));
$t = strtolower(str_replace("`", '', $r[2]));
if ($k && !isset($keys[$k])) {
$keys[$k] = $t;
} else {
$keys[] = $t;
}
}
}
spip_mysql_free($s);
return array('field' => $fields, 'key' => $keys);
}
$res = spip_mysql_query("SHOW COLUMNS FROM `$nom_table`", $serveur);
if ($res) {
$nfields = array();
$nkeys = array();
while ($val = spip_mysql_fetch($res)) {
$nfields[$val["Field"]] = $val['Type'];
if ($val['Null'] == 'NO') {
$nfields[$val["Field"]] .= ' NOT NULL';
}
if ($val['Default'] === '0' || $val['Default']) {
if (preg_match('/[A-Z_]/', $val['Default'])) {
$nfields[$val["Field"]] .= ' DEFAULT ' . $val['Default'];
} else {
$nfields[$val["Field"]] .= " DEFAULT '" . $val['Default'] . "'";
}
}
if ($val['Extra']) {
$nfields[$val["Field"]] .= ' ' . $val['Extra'];
}
if ($val['Key'] == 'PRI') {
$nkeys['PRIMARY KEY'] = $val["Field"];
} else {
if ($val['Key'] == 'MUL') {
$nkeys['KEY ' . $val["Field"]] = $val["Field"];
} else {
if ($val['Key'] == 'UNI') {
$nkeys['UNIQUE KEY ' . $val["Field"]] = $val["Field"];
}
}
}
}
spip_mysql_free($res);
return array('field' => $nfields, 'key' => $nkeys);
}
return "";
}
/**
* Rècupère une ligne de résultat
*
* Récupère la ligne suivante d'une ressource de résultat
*
* @param Ressource $r Ressource de résultat (issu de sql_select)
* @param string $t Structure de résultat attendu (défaut MYSQLI_ASSOC)
* @param string $serveur Nom de la connexion
* @param bool $requeter Inutilisé
* @return array Ligne de résultat
*/
function spip_mysql_fetch($r, $t = '', $serveur = '', $requeter = true) {
if (!$t) {
$t = MYSQLI_ASSOC;
}
if ($r) {
return mysqli_fetch_array($r, $t);
}
}
/**
* Place le pointeur de résultat sur la position indiquée
*
* @param Ressource $r Ressource de résultat
* @param int $row_number Position. Déplacer le pointeur à cette ligne
* @param string $serveur Nom de la connexion
* @param bool $requeter Inutilisé
* @return bool True si déplacement réussi, false sinon.
**/
function spip_mysql_seek($r, $row_number, $serveur = '', $requeter = true) {
if ($r and mysqli_num_rows($r)) {
return mysqli_data_seek($r, $row_number);
}
}
/**
* Retourne le nombre de lignes d'une sélection
*
* @param array|string $from Tables à consulter (From)
* @param array|string $where Conditions a remplir (Where)
* @param array|string $groupby Critère de regroupement (Group by)
* @param array $having Tableau des des post-conditions à remplir (Having)
* @param string $serveur Nom de la connexion
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return int|string
* - String Texte de la requête si demandé
* - int Nombre de lignes (0 si la requête n'a pas réussie)
**/
function spip_mysql_countsel(
$from = array(),
$where = array(),
$groupby = '',
$having = array(),
$serveur = '',
$requeter = true
) {
$c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
$r = spip_mysql_select("COUNT($c)", $from, $where, '', '', '', $having, $serveur, $requeter);
if (!$requeter) {
return $r;
}
if (!$r instanceof mysqli_result) {
return 0;
}
list($c) = mysqli_fetch_array($r, MYSQLI_NUM);
mysqli_free_result($r);
return $c;
}
/**
* Retourne la dernière erreur generée
*
* @note
* Bien spécifier le serveur auquel on s'adresse,
* mais à l'install la globale n'est pas encore complètement définie.
*
* @uses sql_error_backtrace()
*
* @param string $query
* Requête qui était exécutée
* @param string $serveur
* Nom de la connexion
* @param bool $requeter
* Inutilisé
* @return string
* Erreur eventuelle
**/
function spip_mysql_error($query = '', $serveur = '', $requeter = true) {
$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
$s = mysqli_error($link);
if ($s) {
$trace = debug_backtrace();
if ($trace[0]['function'] != "spip_mysql_error") {
spip_log("$s - $query - " . sql_error_backtrace(), 'mysql.' . _LOG_ERREUR);
}
}
return $s;
}
/**
* Retourne le numero de la dernière erreur SQL
*
* @param string $serveur
* Nom de la connexion
* @param bool $requeter
* Inutilisé
* @return int
* 0, pas d'erreur. Autre, numéro de l'erreur.
**/
function spip_mysql_errno($serveur = '', $requeter = true) {
$link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
$s = mysqli_errno($link);
// 2006 MySQL server has gone away
// 2013 Lost connection to MySQL server during query
if (in_array($s, array(2006, 2013))) {
define('spip_interdire_cache', true);
}
if ($s) {
spip_log("Erreur mysql $s", _LOG_ERREUR);
}
return $s;
}
/**
* Retourne le nombre de lignes dune ressource de sélection obtenue
* avec `sql_select()`
*
* @param Ressource $r Ressource de résultat
* @param string $serveur Nom de la connexion
* @param bool $requeter Inutilisé
* @return int Nombre de lignes
*/
function spip_mysql_count($r, $serveur = '', $requeter = true) {
if ($r) {
return mysqli_num_rows($r);
}
}
/**
* Libère une ressource de résultat
*
* Indique à MySQL de libérer de sa mémoire la ressoucre de résultat indiquée
* car on n'a plus besoin de l'utiliser.
*
* @param Ressource $r Ressource de résultat
* @param string $serveur Nom de la connexion
* @param bool $requeter Inutilisé
* @return bool True si réussi
*/
function spip_mysql_free($r, $serveur = '', $requeter = true) {
return (($r instanceof mysqli_result) ? mysqli_free_result($r) : false);
}
/**
* Insère une ligne dans une table
*
* @param string $table
* Nom de la table SQL
* @param string $champs
* Liste des colonnes impactées,
* @param string $valeurs
* Liste des valeurs,
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return bool|string|int|array
* - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
* - Texte de la requête si demandé,
* - False en cas d'erreur,
* - Tableau de description de la requête et du temps d'exécution, si var_profile activé
**/
function spip_mysql_insert($table, $champs, $valeurs, $desc = array(), $serveur = '', $requeter = true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$link = $connexion['link'];
$table = prefixer_table_spip($table, $connexion['prefixe']);
$query = "INSERT INTO $table $champs VALUES $valeurs";
if (!$requeter) {
return $query;
}
if (isset($_GET['var_profile'])) {
include_spip('public/tracer');
$t = trace_query_start();
$e = '';
} else {
$t = 0;
}
$connexion['last'] = $query;
#spip_log($query, 'mysql.'._LOG_DEBUG);
$r = false;
if (mysqli_query($link, $query)) {
$r = mysqli_insert_id($link);
} else {
// Log de l'erreur eventuelle
if ($e = spip_mysql_errno($serveur)) {
$e .= spip_mysql_error($query, $serveur);
} // et du fautif
}
return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
// return $r ? $r : (($r===0) ? -1 : 0); pb avec le multi-base.
}
/**
* Insère une ligne dans une table, en protégeant chaque valeur
*
* @param string $table
* Nom de la table SQL
* @param string $couples
* Couples (colonne => valeur)
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return bool|string|int|array
* - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
* - Texte de la requête si demandé,
* - False en cas d'erreur,
* - Tableau de description de la requête et du temps d'exécution, si var_profile activé
**/
function spip_mysql_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true) {
if (!$desc) {
$desc = description_table($table, $serveur);
}
if (!$desc) {
$couples = array();
}
$fields = isset($desc['field']) ? $desc['field'] : array();
foreach ($couples as $champ => $val) {
$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
}
return spip_mysql_insert($table, "(" . join(',', array_keys($couples)) . ")", "(" . join(',', $couples) . ")", $desc,
$serveur, $requeter);
}
/**
* Insère plusieurs lignes d'un coup dans une table
*
* @param string $table
* Nom de la table SQL
* @param array $tab_couples
* Tableau de tableaux associatifs (colonne => valeur)
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return int|bool|string
* - int|true identifiant du dernier élément inséré (si possible), ou true, si réussite
* - Texte de la requête si demandé,
* - False en cas d'erreur.
**/
function spip_mysql_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true) {
if (!$desc) {
$desc = description_table($table, $serveur);
}
if (!$desc) {
$tab_couples = array();
}
$fields = isset($desc['field']) ? $desc['field'] : array();
$cles = "(" . join(',', array_keys(reset($tab_couples))) . ')';
$valeurs = array();
$r = false;
// Quoter et Inserer par groupes de 100 max pour eviter un debordement de pile
foreach ($tab_couples as $couples) {
foreach ($couples as $champ => $val) {
$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
}
$valeurs[] = '(' . join(',', $couples) . ')';
if (count($valeurs) >= 100) {
$r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
$valeurs = array();
}
}
if (count($valeurs)) {
$r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
}
return $r; // dans le cas d'une table auto_increment, le dernier insert_id
}
/**
* Met à jour des enregistrements d'une table SQL
*
* @param string $table
* Nom de la table
* @param array $champs
* Couples (colonne => valeur)
* @param string|array $where
* Conditions a remplir (Where)
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom de la connexion
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return array|bool|string
* - string : texte de la requête si demandé
* - true si la requête a réussie, false sinon
* - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
*/
function spip_mysql_update($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
$set = array();
foreach ($champs as $champ => $val) {
$set[] = $champ . "=$val";
}
if (!empty($set)) {
return spip_mysql_query(
calculer_mysql_expression('UPDATE', $table, ',')
. calculer_mysql_expression('SET', $set, ',')
. calculer_mysql_expression('WHERE', $where),
$serveur, $requeter);
}
}
/**
* Met à jour des enregistrements d'une table SQL et protège chaque valeur
*
* Protège chaque valeur transmise avec sql_quote(), adapté au type
* de champ attendu par la table SQL
*
* @note
* Les valeurs sont des constantes à mettre entre apostrophes
* sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
*
* @param string $table
* Nom de la table
* @param array $champs
* Couples (colonne => valeur)
* @param string|array $where
* Conditions a remplir (Where)
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom de la connexion
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return array|bool|string
* - string : texte de la requête si demandé
* - true si la requête a réussie, false sinon
* - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
*/
function spip_mysql_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
if (!$champs) {
return;
}
if (!$desc) {
$desc = description_table($table, $serveur);
}
if (!$desc) {
$champs = array();
} else {
$fields = $desc['field'];
}
$set = array();
foreach ($champs as $champ => $val) {
$set[] = $champ . '=' . spip_mysql_cite($val, @$fields[$champ]);
}
return spip_mysql_query(
calculer_mysql_expression('UPDATE', $table, ',')
. calculer_mysql_expression('SET', $set, ',')
. calculer_mysql_expression('WHERE', $where),
$serveur, $requeter);
}
/**
* Supprime des enregistrements d'une table
*
* @param string $table Nom de la table SQL
* @param string|array $where Conditions à vérifier
* @param string $serveur Nom du connecteur
* @param bool $requeter Exécuter la requête, sinon la retourner
* @return bool|string
* - int : nombre de suppressions réalisées,
* - Texte de la requête si demandé,
* - False en cas d'erreur.
**/
function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true) {
$res = spip_mysql_query(
calculer_mysql_expression('DELETE FROM', $table, ',')
. calculer_mysql_expression('WHERE', $where),
$serveur, $requeter);
if (!$requeter) {
return $res;
}
if ($res) {
$link = _mysql_link($serveur);
return mysqli_affected_rows($link);
} else {
return false;
}
}
/**
* Insère où met à jour une entrée dune table SQL
*
* La clé ou les cles primaires doivent être présentes dans les données insérés.
* La fonction effectue une protection automatique des données.
*
* Préférez updateq ou insertq.
*
* @param string $table
* Nom de la table SQL
* @param array $couples
* Couples colonne / valeur à modifier,
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return bool|string
* - true si réussite
* - Texte de la requête si demandé,
* - False en cas d'erreur.
**/
function spip_mysql_replace($table, $couples, $desc = array(), $serveur = '', $requeter = true) {
return spip_mysql_query("REPLACE $table (" . join(',', array_keys($couples)) . ') VALUES (' . join(',',
array_map('_q', $couples)) . ')', $serveur, $requeter);
}
/**
* Insère où met à jour des entrées dune table SQL
*
* La clé ou les cles primaires doivent être présentes dans les données insérés.
* La fonction effectue une protection automatique des données.
*
* Préférez insertq_multi et sql_updateq
*
* @param string $table
* Nom de la table SQL
* @param array $tab_couples
* Tableau de tableau (colonne / valeur à modifier),
* @param array $desc
* Tableau de description des colonnes de la table SQL utilisée
* (il sera calculé si nécessaire s'il n'est pas transmis).
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Exécuter la requête, sinon la retourner
* @return bool|string
* - true si réussite
* - Texte de la requête si demandé,
* - False en cas d'erreur.
**/
function spip_mysql_replace_multi($table, $tab_couples, $desc = array(), $serveur = '', $requeter = true) {
$cles = "(" . join(',', array_keys($tab_couples[0])) . ')';
$valeurs = array();
foreach ($tab_couples as $couples) {
$valeurs[] = '(' . join(',', array_map('_q', $couples)) . ')';
}
$valeurs = implode(', ', $valeurs);
return spip_mysql_query("REPLACE $table $cles VALUES $valeurs", $serveur, $requeter);
}
/**
* Retourne l'instruction SQL pour obtenir le texte d'un champ contenant
* une balise `<multi>` dans la langue indiquée
*
* Cette sélection est mise dans l'alias `multi` (instruction AS multi).
*
* @param string $objet Colonne ayant le texte
* @param string $lang Langue à extraire
* @return string Texte de sélection pour la requête
*/
function spip_mysql_multi($objet, $lang) {
$lengthlang = strlen("[$lang]");
$posmulti = "INSTR(" . $objet . ", '<multi>')";
$posfinmulti = "INSTR(" . $objet . ", '</multi>')";
$debutchaine = "LEFT(" . $objet . ", $posmulti-1)";
$finchaine = "RIGHT(" . $objet . ", CHAR_LENGTH(" . $objet . ") -(7+$posfinmulti))";
$chainemulti = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7, $posfinmulti -(7+$posmulti)))";
$poslang = "INSTR($chainemulti,'[" . $lang . "]')";
$poslang = "IF($poslang=0,INSTR($chainemulti,']')+1,$poslang+$lengthlang)";
$chainelang = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7+$poslang-1,$posfinmulti -($posmulti+7+$poslang-1) ))";
$posfinlang = "INSTR(" . $chainelang . ", '[')";
$chainelang = "IF($posfinlang>0,LEFT($chainelang,$posfinlang-1),$chainelang)";
//$chainelang = "LEFT($chainelang,$posfinlang-1)";
$retour = "(TRIM(IF($posmulti = 0 , " .
" TRIM(" . $objet . "), " .
" CONCAT( " .
" $debutchaine, " .
" IF( " .
" $poslang = 0, " .
" $chainemulti, " .
" $chainelang" .
" ), " .
" $finchaine" .
" ) " .
"))) AS multi";
return $retour;
}
/**
* Prépare une chaîne hexadécimale
*
* Par exemple : FF ==> 0xFF en MySQL
*
* @param string $v
* Chaine hexadecimale
* @return string
* Valeur hexadécimale pour MySQL
**/
function spip_mysql_hex($v) {
return "0x" . $v;
}
/**
* Échapper une valeur selon son type ou au mieux
* comme le fait `_q()` mais pour MySQL avec ses spécificités
*
* @param string|array|number $v
* Texte, nombre ou tableau à échapper
* @param string $type
* Description du type attendu
* (par exemple description SQL de la colonne recevant la donnée)
* @return string|number
* Donnée prête à être utilisée par le gestionnaire SQL
*/
function spip_mysql_quote($v, $type = '') {
if ($type) {
if (!is_array($v)) {
return spip_mysql_cite($v, $type);
}
// si c'est un tableau, le parcourir en propageant le type
foreach ($v as $k => $r) {
$v[$k] = spip_mysql_quote($r, $type);
}
return $v;
}
// si on ne connait pas le type, s'en remettre a _q :
// on ne fera pas mieux
else {
return _q($v);
}
}
/**
* Tester si une date est proche de la valeur d'un champ
*
* @param string $champ
* Nom du champ a tester
* @param int $interval
* Valeur de l'intervalle : -1, 4, ...
* @param string $unite
* Utité utilisée (DAY, MONTH, YEAR, ...)
* @return string
* Expression SQL
**/
function spip_mysql_date_proche($champ, $interval, $unite) {
$use_now = ( ($champ === 'maj' or strpos($champ, '.maj')) ? true : false );
return '('
. $champ
. (($interval <= 0) ? '>' : '<')
. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
. '('
. ($use_now ? 'NOW()' : sql_quote(date('Y-m-d H:i:s')))
. ', INTERVAL '
. (($interval > 0) ? $interval : (0 - $interval))
. ' '
. $unite
. '))';
}
/**
* Retourne une expression IN pour le gestionnaire de base de données
*
* IN (...) est limité à 255 éléments, d'où cette fonction assistante
*
* @param string $val
* Colonne SQL sur laquelle appliquer le test
* @param string|array $valeurs
* Liste des valeurs possibles (séparés par des virgules si string)
* @param string $not
* - '' sélectionne les éléments correspondant aux valeurs
* - 'NOT' inverse en sélectionnant les éléments ne correspondant pas aux valeurs
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Inutilisé
* @return string
* Expression de requête SQL
**/
function spip_mysql_in($val, $valeurs, $not = '', $serveur = '', $requeter = true) {
$n = $i = 0;
$in_sql = "";
while ($n = strpos($valeurs, ',', $n + 1)) {
if ((++$i) >= 255) {
$in_sql .= "($val $not IN (" .
substr($valeurs, 0, $n) .
"))\n" .
($not ? "AND\t" : "OR\t");
$valeurs = substr($valeurs, $n + 1);
$i = $n = 0;
}
}
$in_sql .= "($val $not IN ($valeurs))";
return "($in_sql)";
}
/**
* Retourne une expression IN pour le gestionnaire de base de données
*
* Pour compatibilité. Ne plus utiliser.
*
* @deprecated Utiliser sql_in()
*
* @param string $val Nom de la colonne
* @param string|array $valeurs Valeurs
* @param string $not NOT pour inverser
* @return string Expression de requête SQL
*/
function calcul_mysql_in($val, $valeurs, $not = '') {
if (is_array($valeurs)) {
$valeurs = join(',', array_map('_q', $valeurs));
} elseif ($valeurs[0] === ',') {
$valeurs = substr($valeurs, 1);
}
if (!strlen(trim($valeurs))) {
return ($not ? "0=0" : '0=1');
}
return spip_mysql_in($val, $valeurs, $not);
}
/**
* Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
*
* @param string|number $v Texte ou nombre à échapper
* @param string $type Type de donnée attendue, description SQL de la colonne de destination
* @return string|number Texte ou nombre échappé
*/
function spip_mysql_cite($v, $type) {
if (is_null($v)
and stripos($type, "NOT NULL") === false
) {
return 'NULL';
} // null php se traduit en NULL SQL
if (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
return $v;
}
if (sql_test_int($type)) {
if (is_numeric($v) or (ctype_xdigit(substr($v, 2))
and $v[0] == '0' and $v[1] == 'x')
) {
return $v;
} // si pas numerique, forcer le intval
else {
return intval($v);
}
}
return ("'" . addslashes($v) . "'");
}
// Ces deux fonctions n'ont pas d'equivalent exact PostGres
// et ne sont la que pour compatibilite avec les extensions de SPIP < 1.9.3
/**
* Poser un verrou SQL local
*
* Changer de nom toutes les heures en cas de blocage MySQL (ca arrive)
*
* @deprecated Pas d'équivalence actuellement en dehors de MySQL
* @see spip_release_lock()
*
* @param string $nom
* Inutilisé. Le nom est calculé en fonction de la connexion principale
* @param int $timeout
* @return string|bool
* - Nom du verrou si réussite,
* - false sinon
*/
function spip_get_lock($nom, $timeout = 0) {
define('_LOCK_TIME', intval(time() / 3600 - 316982));
$connexion = &$GLOBALS['connexions'][0];
$bd = $connexion['db'];
$prefixe = $connexion['prefixe'];
$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
$connexion['last'] = $q = "SELECT GET_LOCK(" . _q($nom) . ", $timeout) AS n";
$q = @sql_fetch(mysqli_query(_mysql_link(), $q));
if (!$q) {
spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
}
return $q['n'];
}
/**
* Relâcher un verrou SQL local
*
* @deprecated Pas d'équivalence actuellement en dehors de MySQL
* @see spip_get_lock()
*
* @param string $nom
* Inutilisé. Le nom est calculé en fonction de la connexion principale
* @return string|bool
* True si réussite, false sinon.
*/
function spip_release_lock($nom) {
$connexion = &$GLOBALS['connexions'][0];
$bd = $connexion['db'];
$prefixe = $connexion['prefixe'];
$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
$connexion['last'] = $q = "SELECT RELEASE_LOCK(" . _q($nom) . ")";
mysqli_query(_mysql_link(), $q);
}
/**
* Teste si on a les fonctions MySQLi (pour l'install)
*
* @return bool
* True si on a les fonctions, false sinon
*/
function spip_versions_mysql() {
charger_php_extension('mysqli');
return function_exists('mysqli_query');
}
/**
* Tester si mysql ne veut pas du nom de la base dans les requêtes
*
* @param string $server_db
* @return string
* - chaîne vide si nom de la base utile
* - chaîne : code compilé pour le faire désactiver par SPIP sinon
*/
function test_rappel_nom_base_mysql($server_db) {
$GLOBALS['mysql_rappel_nom_base'] = true;
sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
$ok = spip_query("INSERT INTO spip_meta (nom,valeur) VALUES ('mysql_rappel_nom_base', 'test')", $server_db);
if ($ok) {
sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
return '';
} else {
$GLOBALS['mysql_rappel_nom_base'] = false;
return "\$GLOBALS['mysql_rappel_nom_base'] = false; " .
"/* echec de test_rappel_nom_base_mysql a l'installation. */\n";
}
}
/**
* Teste si on peut changer les modes de MySQL
*
* @link http://dev.mysql.com/doc/refman/5.0/fr/server-sql-mode.html
*
* @param string $server_db Nom de la connexion
* @return string
* - chaîne vide si on ne peut pas appliquer de mode
* - chaîne : code compilé pour l'indiquer le résultat du test à SPIP
*/
function test_sql_mode_mysql($server_db) {
$res = sql_select("version() as v", '', '', '', '', '', '', $server_db);
$row = sql_fetch($res, $server_db);
if (version_compare($row['v'], '5.0.0', '>=')) {
defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE', true);
return "defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE',true);\n";
}
return '';
}