| Current Path : /home/etiennec/www/f1f54/ |
| Current File : /home/etiennec/www/f1f54/req.tar |
sqlite2.php 0000666 00000002611 15250277754 0006662 0 ustar 00 <?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2014 *
* 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. *
\***************************************************************************/
if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('req/sqlite_generique');
$GLOBALS['spip_sqlite2_functions_1'] = _sqlite_ref_fonctions();
// http://doc.spip.org/@req_sqlite2_dist
function req_sqlite2_dist($addr, $port, $login, $pass, $db='', $prefixe='') {
return req_sqlite_dist($addr, $port, $login, $pass, $db, $prefixe, $sqlite_version=2);
}
// http://doc.spip.org/@spip_sqlite2_constantes
function spip_sqlite2_constantes(){
if (!defined('SPIP_SQLITE2_ASSOC')) {
define('SPIP_SQLITE2_ASSOC', SQLITE_ASSOC);
define('SPIP_SQLITE2_NUM', SQLITE_NUM);
define('SPIP_SQLITE2_BOTH', SQLITE_BOTH);
}
}
function spip_versions_sqlite2()
{
return _sqlite_charger_version(2) ? 2 : false;
}
?>
sqlite_fonctions.php 0000666 00000026721 15250277754 0010672 0 ustar 00 <?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2014 *
* 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. *
\***************************************************************************/
if (!defined('_ECRIRE_INC_VERSION')) return;
/*
* Des fonctions pour les requetes SQL
*
* Voir la liste des fonctions natives : http://www.sqlite.org/lang_corefunc.html
* Et la liste des evolutions pour : http://sqlite.org/changes.html
*
*/
// http://doc.spip.org/@_sqlite_init_functions
function _sqlite_init_functions(&$sqlite){
if (!$sqlite) return false;
$fonctions = array(
'CONCAT' => array( '_sqlite_func_concat' ,-1),
'CEIL' => array( '_sqlite_func_ceil', 1), // absent de sqlite2
'DATE_FORMAT' => array( '_sqlite_func_strftime' ,2),
'DAYOFMONTH' => array( '_sqlite_func_dayofmonth' ,1),
'EXTRAIRE_MULTI' => array( '_sqlite_func_extraire_multi', 2), // specifique a SPIP/sql_multi()
'EXP' => array( 'exp' ,1),//exponentielle
'FIND_IN_SET' => array( '_sqlite_func_find_in_set' ,2),
'FLOOR' => array( '_sqlite_func_floor', 1), // absent de sqlite2
'IF' => array( '_sqlite_func_if' ,3),
'INSERT' => array( '_sqlite_func_insert' ,4),
'INSTR' => array( '_sqlite_func_instr' ,2),
'LEAST' => array( '_sqlite_func_least' ,3),
'_LEFT' => array( '_sqlite_func_left' ,2),
# 'LENGTH' => array( 'strlen' ,1), // present v1.0.4
# 'LOWER' => array( 'strtolower' ,1), // present v2.4
# 'LTRIM' => array( 'ltrim' ,1), // present en theorie
'NOW' => array( '_sqlite_func_now' ,0),
'MD5' => array( 'md5' ,1),
'MONTH' => array( '_sqlite_func_month' ,1),
'PREG_REPLACE' => array( '_sqlite_func_preg_replace' ,3),
'RAND' => array( '_sqlite_func_rand' ,0), // sinon random() v2.4
'REGEXP' => array( '_sqlite_func_regexp_match' ,2), // critere REGEXP supporte a partir de v3.3.2
//'REGEXP_MATCH' => array( '_sqlite_func_regexp_match' ,2), // critere REGEXP supporte a partir de v3.3.2
'RIGHT' => array( '_sqlite_func_right' ,2),
# 'RTRIM' => array( 'rtrim' ,1), // present en theorie
'SETTYPE' => array( 'settype' ,2), // CAST present en v3.2.3
'SQRT' => array( 'sqrt' ,1),
'SUBSTRING' => array( '_sqlite_func_substring' /*,3*/), // peut etre appelee avec 2 ou 3 arguments, index base 1 et non 0
'TO_DAYS' => array( '_sqlite_func_to_days' ,1),
# 'TRIM' => array( 'trim' ,1), // present en theorie
'TIMESTAMPDIFF' => array('_sqlite_timestampdiff' /*,3*/),
'UNIX_TIMESTAMP'=> array( '_sqlite_func_unix_timestamp' ,1),
# 'UPPER' => array( 'strtoupper' ,1), // present v2.4
'VIDE' => array( '_sqlite_func_vide' ,0), // du vide pour SELECT 0 as x ... ORDER BY x -> ORDER BY vide()
'YEAR' => array( '_sqlite_func_year' ,1)
);
foreach ($fonctions as $f=>$r){
_sqlite_add_function($sqlite, $f, $r);
}
#spip_log('functions sqlite chargees ','sqlite.'._LOG_DEBUG);
}
// permet au besoin de charger des fonctions ailleurs par _sqlite_init_functions();
// http://doc.spip.org/@_sqlite_add_function
function _sqlite_add_function(&$sqlite, &$f, &$r){
if (_sqlite_is_version(3, $sqlite)){
isset($r[1])
?$sqlite->sqliteCreateFunction($f, $r[0], $r[1])
:$sqlite->sqliteCreateFunction($f, $r[0]);
} else {
isset($r[1])
?sqlite_create_function($sqlite, $f, $r[0], $r[1])
:sqlite_create_function($sqlite, $f, $r[0]);
}
}
//
// SQLite : fonctions sqlite -> php
// entre autre auteurs : mlebas
//
function _sqlite_func_ceil($a) {
return ceil($a);
}
// http://doc.spip.org/@_sqlite_func_concat
function _sqlite_func_concat () {
$args = func_get_args();
return join('',$args);
}
// http://doc.spip.org/@_sqlite_func_dayofmonth
function _sqlite_func_dayofmonth ($d) {
return _sqlite_func_date("d",$d);
}
// http://doc.spip.org/@_sqlite_func_find_in_set
function _sqlite_func_find_in_set($num, $set) {
$rank=0;
foreach (explode(",",$set) as $v) {
if ($v == $num) return (++$rank);
$rank++;
}
return 0;
}
function _sqlite_func_floor($a) {
return floor($a);
}
// http://doc.spip.org/@_sqlite_func_if
function _sqlite_func_if ($bool, $oui, $non) {
return ($bool)?$oui:$non;
}
/*
* INSERT(chaine, index, longueur, chaine) MySQL
* Retourne une chaine de caracteres a partir d'une chaine dans laquelle "sschaine"
* a ete inseree a la position "index" en remplacant "longueur" caracteres.
*/
// http://doc.spip.org/@_sqlite_func_insert
function _sqlite_func_insert ($s, $index, $longueur, $chaine) {
return
substr($s,0, $index)
. $chaine
. substr(substr($s, $index), $longueur);
}
// http://doc.spip.org/@_sqlite_func_instr
function _sqlite_func_instr ($s, $search) {
return strpos($s,$search);
}
// http://doc.spip.org/@_sqlite_func_least
function _sqlite_func_least () {
$arg_list = func_get_args();
$least = min($arg_list);
#spip_log("Passage avec LEAST : $least",'sqlite.'._LOG_DEBUG);
return $least;
}
// http://doc.spip.org/@_sqlite_func_left
function _sqlite_func_left ($s, $lenght) {
return substr($s,0,$lenght);
}
// http://doc.spip.org/@_sqlite_func_now
function _sqlite_func_now(){
static $now = null;
if (is_null($now))
$now = date("Y-m-d H:i:s");
#spip_log("Passage avec NOW : $now",'sqlite.'._LOG_DEBUG);
return $now;
}
// http://doc.spip.org/@_sqlite_func_month
function _sqlite_func_month ($d) {
return _sqlite_func_date("m",$d);
}
// http://doc.spip.org/@_sqlite_func_preg_replace
function _sqlite_func_preg_replace($quoi, $cherche, $remplace) {
$return = preg_replace('%'.$cherche.'%', $remplace, $quoi);
#spip_log("preg_replace : $quoi, $cherche, $remplace, $return",'sqlite.'._LOG_DEBUG);
return $return;
}
/**
* Extrait une langue d'un texte <multi>[fr] xxx [en] yyy</multi>
*
* @param string $quoi le texte contenant ou non un multi
* @param string $lang la langue a extraire
* @return string, l'extrait trouve.
**/
function _sqlite_func_extraire_multi($quoi, $lang) {
if (!defined('_EXTRAIRE_MULTI'))
include_spip('inc/filtres');
if (!function_exists('approcher_langue'))
include_spip('inc/lang');
if (preg_match_all(_EXTRAIRE_MULTI, $quoi, $regs, PREG_SET_ORDER)) {
foreach ($regs as $reg) {
// chercher la version de la langue courante
$trads = extraire_trads($reg[1]);
if ($l = approcher_langue($trads, $lang)) {
$trad = $trads[$l];
} else {
$trad = reset($trads);
}
$quoi = str_replace($reg[0], $trad, $quoi);
}
}
return $quoi;
}
// http://doc.spip.org/@_sqlite_func_rand
function _sqlite_func_rand() {
return rand();
}
// http://doc.spip.org/@_sqlite_func_right
function _sqlite_func_right ($s, $length) {
return substr($s,0 - $length);
}
// http://doc.spip.org/@_sqlite_func_regexp_match
function _sqlite_func_regexp_match($cherche, $quoi) {
// optimiser un cas tres courant avec les requetes en base
if (!$quoi AND !strlen($quoi)) return false;
$u = $GLOBALS['meta']['pcre_u'];
$return = preg_match('%'.$cherche.'%imsS'.$u, $quoi);
#spip_log("regexp_replace : $quoi, $cherche, $remplace, $return",'sqlite.'._LOG_DEBUG);
return $return;
}
// http://doc.spip.org/@_sqlite_func_strftime
function _sqlite_func_strftime($date, $conv){
return strftime($conv, is_int($date)?$date:strtotime($date));
}
/**
* Nombre de jour entre 0000-00-00 et $d
* http://doc.spip.org/@_sqlite_func_to_days
* cf http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_to-days
* @param string $d
* @return int
*/
function _sqlite_func_to_days ($d) {
static $offset = 719528; // nb de jour entre 0000-00-00 et timestamp 0=1970-01-01
$result = $offset+(int)ceil(_sqlite_func_unix_timestamp($d)/(24*3600));
#spip_log("Passage avec TO_DAYS : $d, $result",'sqlite.'._LOG_DEBUG);
return $result;
}
function _sqlite_func_substring($string,$start,$len=null){
// SQL compte a partir de 1, php a partir de 0
$start = ($start>0)?$start-1:$start;
if (is_null($len))
return substr($string,$start);
else
return substr($string,$start,$len);
}
/**
* Calcul de la difference entre 2 timestamp, exprimes dans l'unite fournie en premier argument
* emule https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_timestampdiff
* @param string $unit
* @param string $date1
* @param string $date2
* @return int
*/
function _sqlite_timestampdiff($unit,$date1,$date2){
// PHP >= 5.3
if (function_exists("date_diff")){
$d1 = date_create($date1);
$d2 = date_create($date2);
$diff = date_diff($d1,$d2);
$inv = $diff->invert?-1:1;
switch($unit){
case "YEAR":
return $inv*$diff->y;
case "QUARTER":
return $inv*(4*$diff->y+intval(floor($diff->m/3)));
case "MONTH":
return $inv*(12*$diff->y+$diff->m);
case "WEEK":
return $inv*intval(floor($diff->days/7));
case "DAY":
#var_dump($inv*$diff->days);
return $inv*$diff->days;
case "HOUR":
return $inv*(24*$diff->days+$diff->h);
case "MINUTE":
return $inv*((24*$diff->days+$diff->h)*60+$diff->i);
case "SECOND":
return $inv*(((24*$diff->days+$diff->h)*60+$diff->i)*60+$diff->s);
case "MICROSECOND":
return $inv*(((24*$diff->days+$diff->h)*60+$diff->i)*60+$diff->s)*1000000;
}
return 0;
}
// PHP < 5.3
else {
$d1 = strtotime($date1);
$d2 = strtotime($date2);
$diff = $d2 - $d1;
$sign = ($diff<0?-1:1);
$diff = $sign * $diff;
switch($unit){
case "YEAR":
$diff = $d2-$d1;
return $sign*(date('Y',abs($diff))-date('Y',0));
case "QUARTER":
return $sign*(4*(date('Y',abs($diff))-date('Y',0))+intval(floor((date('m',$diff)-1)/3)));
case "MONTH":
return $sign*((date('Y',$diff)-date('Y',0))*12+date('m',$diff)-1);
case "WEEK":
return intval(floor(($d2-$d1)/3600/7));
case "DAY":
return intval(floor(($d2-$d1)/3600/24));
case "HOUR":
return intval(floor(($d2-$d1)/3600));
case "MINUTE":
return intval(floor(($d2-$d1)/60));
case "SECOND":
return $d2-$d1;
case "MICROSECOND":
return $d2-$d1*1000000;
}
}
}
// http://doc.spip.org/@_sqlite_func_unix_timestamp
function _sqlite_func_unix_timestamp($d) {
static $mem = array();
static $n = 0;
if (isset($mem[$d])) return $mem[$d];
if ($n++>100) {$mem = array();$n=0;}
//2005-12-02 20:53:53
#spip_log("Passage avec UNIX_TIMESTAMP : $d",'sqlite.'._LOG_DEBUG);
if (!$d) return $mem[$d] = mktime();
// une pile plus grosse n'accelere pas le calcul
return $mem[$d] = strtotime($d);
}
// http://doc.spip.org/@_sqlite_func_year
function _sqlite_func_year ($d) {
return _sqlite_func_date("Y",$d);
}
/**
* version optimisee et memoizee de date() utilisee par
* _sqlite_func_year, _sqlite_func_month, _sqlite_func_dayofmonth
* @param string $quoi
* format : Y, m, ou d
* @param int $d
* timestamp
* @return int
*/
function _sqlite_func_date($quoi,$d) {
static $mem = array();
static $n = 0;
if (isset($mem[$d])) return $mem[$d][$quoi];
if ($n++>100) {$mem = array();$n=0;}
$dec = date("Y-m-d",_sqlite_func_unix_timestamp($d));
$mem[$d] = array("Y"=>substr($dec,0,4),"m"=>substr($dec,5,2),"d"=>substr($dec,8,2));
return $mem[$d][$quoi];
}
// http://doc.spip.org/@_sqlite_func_vide
function _sqlite_func_vide(){
return;
}
?>
mysql.php 0000666 00000107550 15250277754 0006454 0 ustar 00 <?php
/* *************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2014 *
* 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 gerant
* les instructions SQL pour MySQL
*
* @package SPIP\SQL\MySQL
*/
if (!defined('_ECRIRE_INC_VERSION')) return;
// fonction pour la premiere connexion a un serveur MySQL
// http://doc.spip.org/@req_mysql_dist
/**
* @param $host
* @param $port
* @param $login
* @param $pass
* @param string $db
* @param string $prefixe
* @return array|bool
*/
function req_mysql_dist($host, $port, $login, $pass, $db='', $prefixe='') {
if (!charger_php_extension('mysql')) return false;
if ($port > 0) $host = "$host:$port";
$link = @mysql_connect($host, $login, $pass, true);
if (!$link) {
spip_log('Echec mysql_connect. Erreur : ' . mysql_error(),'mysql.'._LOG_HS);
return false;
}
$last = '';
if (!$db) {
$ok = $link;
$db = 'spip';
} else {
$ok = spip_mysql_selectdb($db);
if (defined('_MYSQL_SET_SQL_MODE')
OR defined('_MYSQL_SQL_MODE_TEXT_NOT_NULL') // compatibilite
)
mysql_query($last = "set sql_mode=''");
}
spip_log("Connexion vers $host, base $db, prefixe $prefixe " . ($ok ? "operationnelle sur $link" : 'impossible'), _LOG_DEBUG);
return !$ok ? false : array(
'db' => $db,
'last' => $last,
'prefixe' => $prefixe ? $prefixe : $db,
'link' => $GLOBALS['mysql_rappel_connexion'] ? $link : false,
);
}
$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'))
);
// http://doc.spip.org/@spip_mysql_set_charset
/**
* @param $charset
* @param string $serveur
* @param bool $requeter
* @param bool $requeter
* @return resource
*/
function spip_mysql_set_charset($charset, $serveur='',$requeter=true,$requeter=true){
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
spip_log("changement de charset sql : "."SET NAMES "._q($charset), _LOG_DEBUG);
return mysql_query($connexion['last'] = "SET NAMES "._q($charset));
}
// http://doc.spip.org/@spip_mysql_get_charset
/**
* @param array $charset
* @param string $serveur
* @param bool $requeter
* @return array
*
*/
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(mysql_query($c), NULL, $serveur);
}
// obsolete, ne plus utiliser
// http://doc.spip.org/@spip_query_db
function spip_query_db($query, $serveur='',$requeter=true) {
return spip_mysql_query($query, $serveur, $requeter);
}
// Fonction de requete generale, munie d'une trace a la demande
// http://doc.spip.org/@spip_mysql_query
/**
* @param $query
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*
*/
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 = 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;
// 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 @ ".$infos[0] ." | ";
}
$debug .= " " . $_SERVER['REQUEST_URI'].' + '.$GLOBALS['ip'];
$debug = ' /*'.str_replace('*/','@/',$debug).' */';
}
$r = $link ? mysql_query($query.$debug, $link) : mysql_query($query.$debug);
if ($e = spip_mysql_errno($serveur)) // Log de l'erreur eventuelle
$e .= spip_mysql_error($query, $serveur); // et du fautif
return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
}
// http://doc.spip.org/@spip_mysql_alter
/**
* @param $query
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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
}
// http://doc.spip.org/@spip_mysql_optimize
/**
* @param $table
* @param string $serveur
* @param bool $requeter
* @return bool
*/
function spip_mysql_optimize($table, $serveur='',$requeter=true){
spip_mysql_query("OPTIMIZE TABLE ". $table);
return true;
}
// http://doc.spip.org/@spip_mysql_explain
/**
* @param $query
* @param string $serveur
* @param bool $requeter
* @return array
*/
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 ' . traite_query($query, $db, $prefixe);
$r = $link ? mysql_query($query, $link) : mysql_query($query);
return spip_mysql_fetch($r, NULL, $serveur);
}
// fonction instance de sql_select, voir ses specs dans abstract.php
// traite_query pourrait y etre fait d'avance ce serait moins cher
// Les \n et \t sont utiles au debusqueur.
// http://doc.spip.org/@spip_mysql_select
/**
* @param $select
* @param $from
* @param string $where
* @param string $groupby
* @param string $orderby
* @param string $limit
* @param string $having
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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;
}
// 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.
// http://doc.spip.org/@spip_mysql_order
/**
* @param $orderby
* @return string
*/
function spip_mysql_order($orderby)
{
return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
}
// http://doc.spip.org/@calculer_mysql_where
/**
* @param $v
* @return array|mixed|string
*/
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])";
}
}
}
// http://doc.spip.org/@calculer_mysql_expression
/**
* @param $expression
* @param $v
* @param string $join
* @return string
*/
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);
}
}
// http://doc.spip.org/@spip_mysql_select_as
/**
* @param $args
* @return string
*/
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)
// Quand tous les appels SQL seront abstraits on pourra l'ameliorer
define('_SQL_PREFIXE_TABLE', '/([,\s])spip_/S');
// http://doc.spip.org/@traite_query
/**
* @param $query
* @param string $db
* @param string $prefixe
* @return array|null|string
*/
function traite_query($query, $db='', $prefixe='') {
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) {
list($suite,$textes) = query_echappe_textes($suite);
if (preg_match('/^(.*?)([(]\s*SELECT\b.*)$/si', $suite, $r))
$suite = $r[1] . traite_query($r[2], $db, $prefixe);
$suite = query_reinjecte_textes($suite, $textes);
}
}
$r = preg_replace(_SQL_PREFIXE_TABLE, '\1'.$pref, $query) . $suite;
#spip_log("traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG);
return $r;
}
/**
* Selectionne une base de donnees
*
* @param string $nom
* Nom de la base a utiliser
*
* @return bool
* True cas de success.
* False en cas d'erreur.
**/
function spip_mysql_selectdb($db) {
$ok = mysql_select_db($db);
if (!$ok)
spip_log('Echec mysql_selectdb. Erreur : ' . mysql_error(),'mysql.'._LOG_CRITIQUE);
return $ok;
}
/**
* Retourne les bases de donnees accessibles
*
* Retourne un tableau du nom de toutes les bases de donnees
* 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
* Inutilise
* @return array
* Liste de noms de bases de donnees
**/
function spip_mysql_listdbs($serveur='',$requeter=true) {
$dbs = array();
if ($res = spip_mysql_query("SHOW DATABASES")){
while($row = mysql_fetch_assoc($res))
$dbs[] = $row['Database'];
}
return $dbs;
}
// Fonction de creation d'une table SQL nommee $nom
// a partir de 2 tableaux PHP :
// champs: champ => type
// cles: type-de-cle => champ(s)
// si $autoinc, c'est une auto-increment (i.e. serial) sur la Primary Key
// Le nom des caches doit etre inferieur a 64 caracteres
// http://doc.spip.org/@spip_mysql_create
/**
* @param $nom
* @param $champs
* @param $cles
* @param bool $autoinc
* @param bool $temporary
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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");
if (($row = mysql_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" : '') . ")".
($character_set?" DEFAULT $character_set":"")
."\n";
return spip_mysql_query($q, $serveur);
}
/**
* Adapte pour Mysql la declaration SQL d'une colonne d'une table
*
* @param string $query
* Definition SQL d'un champ de table
* @return string
* Definition SQL adaptee 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',
);
$query = preg_replace(array_keys($remplace), $remplace, $query);
return $query;
}
/**
* @param $nom
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
function spip_mysql_create_base($nom, $serveur='',$requeter=true) {
return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
}
// Fonction de creation d'une vue SQL nommee $nom
// http://doc.spip.org/@spip_mysql_create_view
/**
* @param $nom
* @param $query_select
* @param string $serveur
* @param bool $requeter
* @return array|bool|null|resource|string
*/
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);
}
// http://doc.spip.org/@spip_mysql_drop_table
/**
* @param $table
* @param string $exist
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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
// http://doc.spip.org/@spip_mysql_drop_view
/**
* @param $view
* @param string $exist
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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);
}
// http://doc.spip.org/@spip_mysql_repair
/**
* @param $table
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
function spip_mysql_repair($table, $serveur='',$requeter=true)
{
return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
}
define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
// Recupere la definition d'une table ou d'une vue MySQL
// colonnes, indexes, etc.
// au meme format que la definition des tables de SPIP
// http://doc.spip.org/@spip_mysql_showtable
/**
* @param $nom_table
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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) = mysql_fetch_array($s ,MYSQL_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 "";
}
//
// Recuperation des resultats
//
// http://doc.spip.org/@spip_mysql_fetch
/**
* @param $r
* @param string $t
* @param string $serveur
* @param bool $requeter
* @return array
*/
function spip_mysql_fetch($r, $t='', $serveur='',$requeter=true) {
if (!$t) $t = MYSQL_ASSOC;
if ($r) return mysql_fetch_array($r, $t);
}
function spip_mysql_seek($r, $row_number, $serveur='',$requeter=true) {
if ($r and mysql_num_rows($r)) return mysql_data_seek($r,$row_number);
}
// http://doc.spip.org/@spip_mysql_countsel
/**
* @param array $from
* @param array $where
* @param string $groupby
* @param array $having
* @param string $serveur
* @param bool $requeter
* @return array|int|null|resource|string
*
*/
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 (!is_resource($r)) return 0;
list($c) = mysql_fetch_array($r, MYSQL_NUM);
mysql_free_result($r);
return $c;
}
// Bien specifier le serveur auquel on s'adresse,
// mais a l'install la globale n'est pas encore completement definie
// http://doc.spip.org/@spip_mysql_error
/**
* @param string $query
* @param string $serveur
* @param bool $requeter
* @return string
*/
function spip_mysql_error($query='', $serveur='',$requeter=true) {
$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
$s = $link ? mysql_error($link) : mysql_error();
if ($s) spip_log("$s - $query", 'mysql.'._LOG_ERREUR);
return $s;
}
// A transposer dans les portages
// http://doc.spip.org/@spip_mysql_errno
/**
* @param string $serveur
* @param bool $requeter
* @return int
*/
function spip_mysql_errno($serveur='',$requeter=true) {
$link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
$s = $link ? mysql_errno($link) : mysql_errno();
// 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;
}
// Interface de abstract_sql
// http://doc.spip.org/@spip_mysql_count
/**
* @param $r
* @param string $serveur
* @param bool $requeter
* @return int
*/
function spip_mysql_count($r, $serveur='',$requeter=true) {
if ($r) return mysql_num_rows($r);
}
// http://doc.spip.org/@spip_mysql_free
/**
* @param $r
* @param string $serveur
* @param bool $requeter
* @return bool
*/
function spip_mysql_free($r, $serveur='',$requeter=true) {
return (is_resource($r)?mysql_free_result($r):false);
}
// http://doc.spip.org/@spip_mysql_insert
/**
* @param $table
* @param $champs
* @param $valeurs
* @param string $desc
* @param string $serveur
* @param bool $requeter
* @return int|string
*/
function spip_mysql_insert($table, $champs, $valeurs, $desc='', $serveur='',$requeter=true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
$query ="INSERT INTO $table $champs VALUES $valeurs";
if (!$requeter) return $query;
if (isset($_GET['var_profile'])) {
include_spip('public/tracer');
$t = trace_query_start();
} else $t = 0 ;
$connexion['last'] = $query;
#spip_log($query, 'mysql.'._LOG_DEBUG);
if (mysql_query($query, $link))
$r = mysql_insert_id($link);
else {
if ($e = spip_mysql_errno($serveur)) // Log de l'erreur eventuelle
$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.
}
// http://doc.spip.org/@spip_mysql_insertq
/**
* @param $table
* @param array $couples
* @param array $desc
* @param string $serveur
* @param bool $requeter
* @return int|string
*/
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);
}
// http://doc.spip.org/@spip_mysql_insertq_multi
/**
* @param $table
* @param array $tab_couples
* @param array $desc
* @param string $serveur
* @param bool $requeter
* @return bool|int|string
*/
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
}
// http://doc.spip.org/@spip_mysql_update
/**
* @param $table
* @param $champs
* @param string $where
* @param string $desc
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
function spip_mysql_update($table, $champs, $where='', $desc='', $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);
}
// idem, mais les valeurs sont des constantes a mettre entre apostrophes
// sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
// http://doc.spip.org/@spip_mysql_updateq
/**
* @param $table
* @param $champs
* @param string $where
* @param array $desc
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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);
}
// http://doc.spip.org/@spip_mysql_delete
/**
* @param $table
* @param string $where
* @param string $serveur
* @param bool $requeter
* @return array|bool|int|null|resource|string
*/
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){
$connexion = &$GLOBALS['connexions'][$serveur ? $serveur : 0];
$link = $connexion['link'];
return $link ? mysql_affected_rows($link) : mysql_affected_rows();
}
else
return false;
}
// http://doc.spip.org/@spip_mysql_replace
/**
* @param $table
* @param $couples
* @param array $desc
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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);
}
// http://doc.spip.org/@spip_mysql_replace_multi
/**
* @param $table
* @param $tab_couples
* @param array $desc
* @param string $serveur
* @param bool $requeter
* @return array|null|resource|string
*/
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);
}
// http://doc.spip.org/@spip_mysql_multi
/**
* @param $objet
* @param $lang
* @return string
*
*/
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;
}
// http://doc.spip.org/@spip_mysql_hex
/**
* @param $v
* @return string
*/
function spip_mysql_hex($v)
{
return "0x" . $v;
}
/**
* @param $v
* @param string $type
* @return array|int|string
*/
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);
}
/**
* @param $champ
* @param $interval
* @param $unite
* @return string
*/
function spip_mysql_date_proche($champ, $interval, $unite)
{
return '('
. $champ
. (($interval <= 0) ? '>' : '<')
. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
. '('
. sql_quote(date('Y-m-d H:i:s'))
. ', INTERVAL '
. (($interval > 0) ? $interval : (0-$interval))
. ' '
. $unite
. '))';
}
//
// IN (...) est limite a 255 elements, d'ou cette fonction assistante
//
// http://doc.spip.org/@spip_mysql_in
/**
* @param $val
* @param $valeurs
* @param string $not
* @param string $serveur
* @param bool $requeter
* @return string
*/
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)";
}
// pour compatibilite. Ne plus utiliser.
// http://doc.spip.org/@calcul_mysql_in
/**
* @param $val
* @param $valeurs
* @param string $not
* @return string
*/
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);
}
// http://doc.spip.org/@spip_mysql_cite
/**
* @param $v
* @param $type
* @return int|string
*/
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 local a un SPIP donne
// Changer de nom toutes les heures en cas de blocage MySQL (ca arrive)
//
// http://doc.spip.org/@spip_get_lock
/**
* @param $nom
* @param int $timeout
* @return mixed
*/
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(mysql_query($q));
if (!$q) spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
return $q['n'];
}
// http://doc.spip.org/@spip_release_lock
/**
* @param $nom
*/
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) . ")";
@mysql_query($q);
}
// Renvoie false si on n'a pas les fonctions mysql (pour l'install)
// http://doc.spip.org/@spip_versions_mysql
/**
* @return bool
*/
function spip_versions_mysql() {
charger_php_extension('mysql');
return function_exists('mysql_query');
}
// Tester si mysql ne veut pas du nom de la base dans les requetes
// http://doc.spip.org/@test_rappel_nom_base_mysql
/**
* @param $server_db
* @return string
*/
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";
}
}
// http://doc.spip.org/@test_sql_mode_mysql
/**
* @param $server_db
* @return string
*/
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','>=')){
define('_MYSQL_SET_SQL_MODE',true);
return "define('_MYSQL_SET_SQL_MODE',true);\n";
}
return '';
}
?>
sqlite_generique.php 0000666 00000217440 15250277754 0010654 0 ustar 00 <?php
/* *************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2014 *
* 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 gerant
* les instructions SQL pour Sqlite
*
* @package SPIP\SQL\SQLite
*/
if (!defined('_ECRIRE_INC_VERSION')) return;
// TODO: get/set_caracteres ?
/*
*
* regroupe le maximum de fonctions qui peuvent cohabiter
* D'abord les fonctions d'abstractions de SPIP
*
*/
// http://doc.spip.org/@req_sqlite_dist
function req_sqlite_dist($addr, $port, $login, $pass, $db = '', $prefixe = '', $sqlite_version = ''){
static $last_connect = array();
// si provient de selectdb
// un code pour etre sur que l'on vient de select_db()
if (strpos($db, $code = '@selectdb@')!==false){
foreach (array('addr', 'port', 'login', 'pass', 'prefixe') as $a){
$$a = $last_connect[$a];
}
$db = str_replace($code, '', $db);
}
/*
* En sqlite, seule l'adresse du fichier est importante.
* Ce sera $db le nom,
* le path est $addr
* (_DIR_DB si $addr est vide)
*/
_sqlite_init();
// determiner le dossier de la base : $addr ou _DIR_DB
$f = _DIR_DB;
if ($addr AND strpos($addr, '/')!==false)
$f = rtrim($addr, '/').'/';
// un nom de base demande et impossible d'obtenir la base, on s'en va :
// il faut que la base existe ou que le repertoire parent soit writable
if ($db AND !is_file($f .= $db.'.sqlite') AND !is_writable(dirname($f))){
spip_log("base $f non trouvee ou droits en ecriture manquants", 'sqlite.'._LOG_HS);
return false;
}
// charger les modules sqlite au besoin
if (!_sqlite_charger_version($sqlite_version)){
spip_log("Impossible de trouver/charger le module SQLite ($sqlite_version)!", 'sqlite.'._LOG_HS);
return false;
}
// chargement des constantes
// il ne faut pas definir les constantes avant d'avoir charge les modules sqlite
$define = "spip_sqlite".$sqlite_version."_constantes";
$define();
$ok = false;
if (!$db){
// si pas de db ->
// base temporaire tant qu'on ne connait pas son vrai nom
// pour tester la connexion
$db = "_sqlite".$sqlite_version."_install";
$tmp = _DIR_DB.$db.".sqlite";
if ($sqlite_version==3){
$ok = $link = new PDO("sqlite:$tmp");
} else {
$ok = $link = sqlite_open($tmp, _SQLITE_CHMOD, $err);
}
} else {
// Ouvrir (eventuellement creer la base)
// si pas de version fourni, on essaie la 3, sinon la 2
if ($sqlite_version==3){
$ok = $link = new PDO("sqlite:$f");
} else {
$ok = $link = sqlite_open($f, _SQLITE_CHMOD, $err);
}
}
if (!$ok){
$e = sqlite_last_error($db);
spip_log("Impossible d'ouvrir la base SQLite($sqlite_version) $f : $e", 'sqlite.'._LOG_HS);
return false;
}
if ($link){
$last_connect = array(
'addr' => $addr,
'port' => $port,
'login' => $login,
'pass' => $pass,
'db' => $db,
'prefixe' => $prefixe,
);
// etre sur qu'on definit bien les fonctions a chaque nouvelle connexion
include_spip('req/sqlite_fonctions');
_sqlite_init_functions($link);
}
return array(
'db' => $db,
'prefixe' => $prefixe ? $prefixe : $db,
'link' => $link,
);
}
/**
* Fonction de requete generale, munie d'une trace a la demande
*
* @param string $query
* Requete a executer
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Effectuer la requete ?
* - true pour executer
* - false pour retourner le texte de la requete
* @return bool|SQLiteResult|string
* Resultat de la requete
*/
function spip_sqlite_query($query, $serveur = '', $requeter = true){
#spip_log("spip_sqlite_query() > $query",'sqlite.'._LOG_DEBUG);
#_sqlite_init(); // fait la premiere fois dans spip_sqlite
$query = spip_sqlite::traduire_requete($query, $serveur);
if (!$requeter) return $query;
return spip_sqlite::executer_requete($query, $serveur);
}
/* ordre alphabetique pour les autres */
// http://doc.spip.org/@spip_sqlite_alter
function spip_sqlite_alter($query, $serveur = '', $requeter = true){
$query = spip_sqlite_query("ALTER $query", $serveur, false);
// traduire la requete pour recuperer les bons noms de table
$query = spip_sqlite::traduire_requete($query, $serveur);
/*
* la il faut faire les transformations
* si ALTER TABLE x (DROP|CHANGE) y
*
* 1) recuperer "ALTER TABLE table "
* 2) spliter les sous requetes (,)
* 3) faire chaque requete independemment
*/
// 1
if (preg_match("/\s*(ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
$debut = $regs[1];
$table = $regs[3];
$suite = $regs[4];
} else {
spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite.'._LOG_ERREUR);
return false;
}
// 2
// il faudrait une regexp pour eviter de spliter ADD PRIMARY KEY (colA, colB)
// tout en cassant "ADD PRIMARY KEY (colA, colB), ADD INDEX (chose)"... en deux
// ou revoir l'api de sql_alter en creant un
// sql_alter_table($table,array($actions));
$todo = explode(',', $suite);
// on remet les morceaux dechires ensembles... que c'est laid !
$todo2 = array();
$i = 0;
$ouverte = false;
while ($do = array_shift($todo)){
$todo2[$i] = isset($todo2[$i]) ? $todo2[$i].",".$do : $do;
$o = (false!==strpos($do, "("));
$f = (false!==strpos($do, ")"));
if ($o AND !$f) $ouverte = true;
elseif ($f) $ouverte = false;
if (!$ouverte) $i++;
}
// 3
$resultats = array();
foreach ($todo2 as $do){
$do = trim($do);
if (!preg_match('/(DROP PRIMARY KEY|DROP KEY|DROP INDEX|DROP COLUMN|DROP'
.'|CHANGE COLUMN|CHANGE|MODIFY|RENAME TO|RENAME'
.'|ADD PRIMARY KEY|ADD KEY|ADD INDEX|ADD UNIQUE KEY|ADD UNIQUE'
.'|ADD COLUMN|ADD'
.')\s*([^\s]*)\s*(.*)?/i', $do, $matches)){
spip_log("SQLite : Probleme de ALTER TABLE, utilisation non reconnue dans : $do \n(requete d'origine : $query)", 'sqlite.'._LOG_ERREUR);
return false;
}
$cle = strtoupper($matches[1]);
$colonne_origine = $matches[2];
$colonne_destination = '';
$def = $matches[3];
// eluder une eventuelle clause before|after|first inutilisable
$defr = rtrim(preg_replace('/(BEFORE|AFTER|FIRST)(.*)$/is', '', $def));
$defo = $defr; // garder la def d'origine pour certains cas
// remplacer les definitions venant de mysql
$defr = _sqlite_remplacements_definitions_table($defr);
// reinjecter dans le do
$do = str_replace($def, $defr, $do);
$def = $defr;
switch ($cle) {
// suppression d'un index
case 'DROP KEY':
case 'DROP INDEX':
$nom_index = $colonne_origine;
spip_sqlite_drop_index($nom_index, $table, $serveur);
break;
// suppression d'une pk
case 'DROP PRIMARY KEY':
if (!_sqlite_modifier_table(
$table,
$colonne_origine,
array('key' => array('PRIMARY KEY' => '')),
$serveur)){
return false;
}
break;
// suppression d'une colonne
case 'DROP COLUMN':
case 'DROP':
if (!_sqlite_modifier_table(
$table,
array($colonne_origine => ""),
'',
$serveur)){
return false;
}
break;
case 'CHANGE COLUMN':
case 'CHANGE':
// recuperer le nom de la future colonne
// on reprend la def d'origine car _sqlite_modifier_table va refaire la translation
// en tenant compte de la cle primaire (ce qui est mieux)
$def = trim($defo);
$colonne_destination = substr($def, 0, strpos($def, ' '));
$def = substr($def, strlen($colonne_destination)+1);
if (!_sqlite_modifier_table(
$table,
array($colonne_origine => $colonne_destination),
array('field' => array($colonne_destination => $def)),
$serveur)){
return false;
}
break;
case 'MODIFY':
// on reprend la def d'origine car _sqlite_modifier_table va refaire la translation
// en tenant compte de la cle primaire (ce qui est mieux)
if (!_sqlite_modifier_table(
$table,
$colonne_origine,
array('field' => array($colonne_origine => $defo)),
$serveur)){
return false;
}
break;
// pas geres en sqlite2
case 'RENAME':
$do = "RENAME TO".substr($do, 6);
case 'RENAME TO':
if (_sqlite_is_version(3, '', $serveur)){
if (!spip_sqlite::executer_requete("$debut $do", $serveur)){
spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite.'._LOG_ERREUR);
return false;
}
// artillerie lourde pour sqlite2 !
} else {
$table_dest = trim(substr($do, 9));
if (!_sqlite_modifier_table(array($table => $table_dest), '', '', $serveur)){
spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite.'._LOG_ERREUR);
return false;
}
}
break;
// ajout d'une pk
case 'ADD PRIMARY KEY':
$pk = trim(substr($do, 16));
$pk = ($pk[0]=='(') ? substr($pk, 1, -1) : $pk;
if (!_sqlite_modifier_table(
$table,
$colonne_origine,
array('key' => array('PRIMARY KEY' => $pk)),
$serveur)){
return false;
}
break;
// ajout d'un index
case 'ADD UNIQUE KEY':
case 'ADD UNIQUE':
$unique=true;
case 'ADD INDEX':
case 'ADD KEY':
// peut etre "(colonne)" ou "nom_index (colonnes)"
// bug potentiel si qqn met "(colonne, colonne)"
//
// nom_index (colonnes)
if ($def){
$colonnes = substr($def, 1, -1);
$nom_index = $colonne_origine;
}
else {
// (colonne)
if ($colonne_origine[0]=="("){
$colonnes = substr($colonne_origine, 1, -1);
if (false!==strpos(",", $colonnes)){
spip_log(_LOG_GRAVITE_ERREUR, "SQLite : Erreur, impossible de creer un index sur plusieurs colonnes"
." sans qu'il ait de nom ($table, ($colonnes))", 'sqlite');
break;
} else {
$nom_index = $colonnes;
}
}
// nom_index
else {
$nom_index = $colonnes = $colonne_origine;
}
}
spip_sqlite_create_index($nom_index, $table, $colonnes, $unique, $serveur);
break;
// pas geres en sqlite2
case 'ADD COLUMN':
$do = "ADD".substr($do, 10);
case 'ADD':
default:
if (_sqlite_is_version(3, '', $serveur) AND !preg_match(',primary\s+key,i',$do)){
if (!spip_sqlite::executer_requete("$debut $do", $serveur)){
spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite.'._LOG_ERREUR);
return false;
}
break;
}
// artillerie lourde pour sqlite2 !
// ou si la colonne est aussi primary key
// cas du add id_truc int primary key
// ajout d'une colonne qui passe en primary key directe
else {
$def = trim(substr($do, 3));
$colonne_ajoutee = substr($def, 0, strpos($def, ' '));
$def = substr($def, strlen($colonne_ajoutee)+1);
$opts = array();
if (preg_match(',primary\s+key,i',$def)){
$opts['key'] = array('PRIMARY KEY' => $colonne_ajoutee);
$def = preg_replace(',primary\s+key,i','',$def);
}
$opts['field'] = array($colonne_ajoutee => $def);
if (!_sqlite_modifier_table($table, array($colonne_ajoutee), $opts, $serveur)){
spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite.'._LOG_ERREUR);
return false;
}
}
break;
}
// tout est bon, ouf !
spip_log("SQLite ($serveur) : Changements OK : $debut $do", 'sqlite.'._LOG_INFO);
}
spip_log("SQLite ($serveur) : fin ALTER TABLE OK !", 'sqlite.'._LOG_INFO);
return true;
}
/**
* Fonction de creation d'une table SQL nommee $nom
* http://doc.spip.org/@spip_sqlite_create
*
* @param string $nom
* @param array $champs
* @param array $cles
* @param bool $autoinc
* @param bool $temporary
* @param string $serveur
* @param bool $requeter
* @return bool|SQLiteResult|string
*/
function spip_sqlite_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $serveur = '', $requeter = true){
$query = _sqlite_requete_create($nom, $champs, $cles, $autoinc, $temporary, $ifnotexists = true, $serveur, $requeter);
if (!$query) return false;
$res = spip_sqlite_query($query, $serveur, $requeter);
// SQLite ne cree pas les KEY sur les requetes CREATE TABLE
// il faut donc les faire creer ensuite
if (!$requeter) return $res;
$ok = $res ? true : false;
if ($ok){
foreach ($cles as $k => $v){
if (preg_match(',^(KEY|UNIQUE)\s,i',$k,$m)){
$index = trim(substr($k,strlen($m[1])));
$unique = (strlen($m[1])>3);
$ok &= spip_sqlite_create_index($index, $nom, $v, $unique, $serveur);
}
}
}
return $ok ? true : false;
}
/**
* Fonction pour creer une base de donnees SQLite
*
* @param string $nom le nom de la base (sans l'extension de fichier)
* @param string $serveur le nom de la connexion
* @param string $option options
*
* @return bool true si la base est creee.
**/
function spip_sqlite_create_base($nom, $serveur = '', $option = true){
$f = $nom.'.sqlite';
if (strpos($nom, "/")===false)
$f = _DIR_DB.$f;
if (_sqlite_is_version(2, '', $serveur)){
$ok = sqlite_open($f, _SQLITE_CHMOD, $err);
} else {
$ok = new PDO("sqlite:$f");
}
if ($ok){
unset($ok);
return true;
}
unset($ok);
return false;
}
/**
* Fonction de creation d'une vue SQL nommee $nom
* http://doc.spip.org/@spip_sqlite_create_view
*
* @param string $nom
* Nom de la vue a creer
* @param string $query_select
* Texte de la requete de selection servant de base a la vue
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Effectuer la requete ?
* - true pour executer
* - false pour retourner le texte de la requete
* @return bool|SQLiteResult|string
* Resultat de la requete ou
* - false si erreur ou si la vue existe deja
* - string texte de la requete si $requeter vaut false
*/
function spip_sqlite_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)", 'sqlite.'._LOG_ERREUR);
return false;
}
$query = "CREATE VIEW $nom AS ".$query_select;
return spip_sqlite_query($query, $serveur, $requeter);
}
/**
* Fonction de creation d'un INDEX
*
* @param string $nom : nom de l'index
* @param string $table : table sql de l'index
* @param string/array $champs : liste de champs sur lesquels s'applique l'index
* @param string $serveur : nom de la connexion sql utilisee
* @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
*
* @return bool ou requete
*/
function spip_sqlite_create_index($nom, $table, $champs, $unique='', $serveur = '', $requeter = true){
if (!($nom OR $table OR $champs)){
spip_log("Champ manquant pour creer un index sqlite ($nom, $table, (".join(',', $champs)."))", 'sqlite.'._LOG_ERREUR);
return false;
}
// SQLite ne differentie pas noms des index en fonction des tables
// il faut donc creer des noms uniques d'index pour une base sqlite
$nom = $table.'_'.$nom;
// enlever d'eventuelles parentheses deja presentes sur champs
if (!is_array($champs)){
if ($champs[0]=="(") $champs = substr($champs, 1, -1);
$champs = array($champs);
// supprimer l'info de longueur d'index mysql en fin de champ
$champs = preg_replace(",\(\d+\)$,","",$champs);
}
$ifnotexists = "";
$version = spip_sqlite_fetch(spip_sqlite_query("select sqlite_version() AS sqlite_version",$serveur),'',$serveur);
if (!function_exists('spip_version_compare')) include_spip('plugins/installer');
if ($version AND spip_version_compare($version['sqlite_version'],'3.3.0','>=')) {
$ifnotexists = ' IF NOT EXISTS';
} else {
/* simuler le IF EXISTS - version 2 et sqlite < 3.3a */
$a = spip_sqlite_showtable($table, $serveur);
if (isset($a['key']['KEY '.$nom])) return true;
}
$query = "CREATE ".($unique?"UNIQUE ":"")."INDEX$ifnotexists $nom ON $table (".join(',', $champs).")";
$res = spip_sqlite_query($query, $serveur, $requeter);
if (!$requeter) return $res;
if ($res)
return true;
else
return false;
}
/**
* en PDO/sqlite3, il faut calculer le count par une requete count(*)
* pour les resultats de SELECT
* cela est fait sans spip_sqlite_query()
* http://doc.spip.org/@spip_sqlite_count
*
* @param $r
* @param string $serveur
* @param bool $requeter
* @return int
*/
function spip_sqlite_count($r, $serveur = '', $requeter = true){
if (!$r) return 0;
if (_sqlite_is_version(3, '', $serveur)){
// select ou autre (insert, update,...) ?
// (link,requete) a compter
if (is_array($r->spipSqliteRowCount)){
list($link,$query) = $r->spipSqliteRowCount;
// amelioration possible a tester intensivement : pas de order by pour compter !
// $query = preg_replace(",ORDER BY .+(LIMIT\s|HAVING\s|GROUP BY\s|$),Uims","\\1",$query);
$query = "SELECT count(*) as zzzzsqlitecount FROM ($query)";
$l = $link->query($query);
$i = 0;
if ($l AND $z = $l->fetch())
$i = $z['zzzzsqlitecount'];
$r->spipSqliteRowCount = $i;
}
if (isset($r->spipSqliteRowCount)){
// Ce compte est faux s'il y a des limit dans la requete :(
// il retourne le nombre d'enregistrements sans le limit
return $r->spipSqliteRowCount;
} else {
return $r->rowCount();
}
} else {
return sqlite_num_rows($r);
}
}
// http://doc.spip.org/@spip_sqlite_countsel
function spip_sqlite_countsel($from = array(), $where = array(), $groupby = '', $having = array(), $serveur = '', $requeter = true){
$c = !$groupby ? '*' : ('DISTINCT '.(is_string($groupby) ? $groupby : join(',', $groupby)));
$r = spip_sqlite_select("COUNT($c)", $from, $where, '', '', '',
$having, $serveur, $requeter);
if ((is_resource($r) or is_object($r)) && $requeter){ // ressource : sqlite2, object : sqlite3
if (_sqlite_is_version(3, '', $serveur)){
list($r) = spip_sqlite_fetch($r, SPIP_SQLITE3_NUM, $serveur);
} else {
list($r) = spip_sqlite_fetch($r, SPIP_SQLITE2_NUM, $serveur);
}
}
return $r;
}
// http://doc.spip.org/@spip_sqlite_delete
function spip_sqlite_delete($table, $where = '', $serveur = '', $requeter = true){
$res = spip_sqlite_query(
_sqlite_calculer_expression('DELETE FROM', $table, ',')
._sqlite_calculer_expression('WHERE', $where),
$serveur, $requeter);
// renvoyer la requete inerte si demandee
if (!$requeter) return $res;
if ($res){
$link = _sqlite_link($serveur);
if (_sqlite_is_version(3, $link)){
return $res->rowCount();
} else {
return sqlite_changes($link);
}
}
else
return false;
}
// http://doc.spip.org/@spip_sqlite_drop_table
function spip_sqlite_drop_table($table, $exist = '', $serveur = '', $requeter = true){
if ($exist) $exist = " IF EXISTS";
/* simuler le IF EXISTS - version 2 */
if ($exist && _sqlite_is_version(2, '', $serveur)){
$a = spip_sqlite_showtable($table, $serveur);
if (!$a) return true;
$exist = '';
}
if (spip_sqlite_query("DROP TABLE$exist $table", $serveur, $requeter))
return true;
else
return false;
}
/**
* supprime une vue
* http://doc.spip.org/@spip_sqlite_drop_view
*
* @param $view
* @param string $exist
* @param string $serveur
* @param bool $requeter
* @return bool|SQLiteResult|string
*/
function spip_sqlite_drop_view($view, $exist = '', $serveur = '', $requeter = true){
if ($exist) $exist = " IF EXISTS";
/* simuler le IF EXISTS - version 2 */
if ($exist && _sqlite_is_version(2, '', $serveur)){
$a = spip_sqlite_showtable($view, $serveur);
if (!$a) return true;
$exist = '';
}
return spip_sqlite_query("DROP VIEW$exist $view", $serveur, $requeter);
}
/**
* Fonction de suppression d'un INDEX
*
* @param string $nom : nom de l'index
* @param string $table : table sql de l'index
* @param string $serveur : nom de la connexion sql utilisee
* @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
*
* @return bool ou requete
*/
function spip_sqlite_drop_index($nom, $table, $serveur = '', $requeter = true){
if (!($nom OR $table)){
spip_log("Champ manquant pour supprimer un index sqlite ($nom, $table)", 'sqlite.'._LOG_ERREUR);
return false;
}
// SQLite ne differentie pas noms des index en fonction des tables
// il faut donc creer des noms uniques d'index pour une base sqlite
$index = $table.'_'.$nom;
$exist = " IF EXISTS";
/* simuler le IF EXISTS - version 2 */
if (_sqlite_is_version(2, '', $serveur)){
$a = spip_sqlite_showtable($table, $serveur);
if (!isset($a['key']['KEY '.$nom])) return true;
$exist = '';
}
$query = "DROP INDEX$exist $index";
return spip_sqlite_query($query, $serveur, $requeter);
}
/**
* Retourne la dernière erreur generée
*
* @param $serveur
* nom de la connexion
* @return string
* erreur eventuelle
**/
function spip_sqlite_error($query = '', $serveur = ''){
$link = _sqlite_link($serveur);
if (_sqlite_is_version(3, $link)){
$errs = $link->errorInfo();
/*
$errs[0]
numero SQLState ('HY000' souvent lors d'une erreur)
http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
$errs[1]
numéro d'erreur SQLite (souvent 1 lors d'une erreur)
http://www.sqlite.org/c3ref/c_abort.html
$errs[2]
Le texte du message d'erreur
*/
$s = '';
if (ltrim($errs[0],'0')) { // 00000 si pas d'erreur
$s = "$errs[2]";
}
} elseif ($link) {
$s = sqlite_error_string(sqlite_last_error($link));
} else {
$s = ": aucune ressource sqlite (link)";
}
if ($s) spip_log("$s - $query", 'sqlite.'._LOG_ERREUR);
return $s;
}
/**
* Retourne le numero de la dernière erreur SQL
*
* Le numéro (en sqlite3/pdo) est un retour ODBC tel que (très souvent) HY000
* http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
*
* @param string $serveur
* nom de la connexion
* @return int|string
* 0 pas d'erreur
* 1 ou autre erreur (en sqlite 2)
* 'HY000/1' : numéro de l'erreur SQLState / numéro d'erreur interne SQLite (en sqlite 3)
**/
function spip_sqlite_errno($serveur = ''){
$link = _sqlite_link($serveur);
if (_sqlite_is_version(3, $link)){
$t = $link->errorInfo();
$s = ltrim($t[0],'0'); // 00000 si pas d'erreur
if ($s) $s .= ' / ' . $t[1]; // ajoute l'erreur du moteur SQLite
} elseif ($link) {
$s = sqlite_last_error($link);
} else {
$s = ": aucune ressource sqlite (link)";
}
if ($s) spip_log("Erreur sqlite $s", 'sqlite.'._LOG_ERREUR);
return $s ? $s : 0;
}
// http://doc.spip.org/@spip_sqlite_explain
function spip_sqlite_explain($query, $serveur = '', $requeter = true){
if (strpos(ltrim($query), 'SELECT')!==0) return array();
$query = spip_sqlite::traduire_requete($query, $serveur);
$query = 'EXPLAIN '.$query;
if (!$requeter) return $query;
// on ne trace pas ces requetes, sinon on obtient un tracage sans fin...
$r = spip_sqlite::executer_requete($query, $serveur, false);
return $r ? spip_sqlite_fetch($r, null, $serveur) : false; // hum ? etrange ca... a verifier
}
// http://doc.spip.org/@spip_sqlite_fetch
function spip_sqlite_fetch($r, $t = '', $serveur = '', $requeter = true){
$link = _sqlite_link($serveur);
$is_v3 = _sqlite_is_version(3, $link);
if (!$t)
$t = ($is_v3 ? SPIP_SQLITE3_ASSOC : SPIP_SQLITE2_ASSOC);
$retour = false;
if ($r)
$retour = ($is_v3 ? $r->fetch($t) : sqlite_fetch_array($r, $t));
// les version 2 et 3 parfois renvoie des 'table.titre' au lieu de 'titre' tout court ! pff !
// suppression de 'table.' pour toutes les cles (c'est un peu violent !)
// c'est couteux : on ne verifie que la premiere ligne pour voir si on le fait ou non
if ($retour
AND strpos(implode('',array_keys($retour)),'.')!==false){
foreach ($retour as $cle => $val){
if (($pos = strpos($cle, '.'))!==false){
$retour[substr($cle, $pos+1)] = &$retour[$cle];
unset($retour[$cle]);
}
}
}
return $retour;
}
function spip_sqlite_seek($r, $row_number, $serveur = '', $requeter = true){
if ($r){
$link = _sqlite_link($serveur);
if (_sqlite_is_version(3, $link)){
// encore un truc de bien fichu : PDO ne PEUT PAS faire de seek ou de rewind...
// je me demande si pour sqlite 3 il ne faudrait pas mieux utiliser
// les nouvelles fonctions sqlite3_xx (mais encore moins presentes...)
return false;
}
else {
return sqlite_seek($r, $row_number);
}
}
}
// http://doc.spip.org/@spip_sqlite_free
function spip_sqlite_free(&$r, $serveur = '', $requeter = true){
unset($r);
return true;
//return sqlite_free_result($r);
}
// http://doc.spip.org/@spip_sqlite_get_charset
function spip_sqlite_get_charset($charset = array(), $serveur = '', $requeter = true){
//$c = !$charset ? '' : (" LIKE "._q($charset['charset']));
//return spip_sqlite_fetch(sqlite_query(_sqlite_link($serveur), "SHOW CHARACTER SET$c"), NULL, $serveur);
}
// http://doc.spip.org/@spip_sqlite_hex
function spip_sqlite_hex($v){
return hexdec($v);
}
// http://doc.spip.org/@spip_sqlite_in
function spip_sqlite_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)";
}
// http://doc.spip.org/@spip_sqlite_insert
function spip_sqlite_insert($table, $champs, $valeurs, $desc = '', $serveur = '', $requeter = true){
$query = "INSERT INTO $table ".($champs ? "$champs VALUES $valeurs" : "DEFAULT VALUES");
if ($r = spip_sqlite_query($query, $serveur, $requeter)){
if (!$requeter) return $r;
$nb = spip_sqlite::last_insert_id($serveur);
}
else
$nb = 0;
$err = spip_sqlite_error($query, $serveur);
// cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
return isset($_GET['var_profile']) ? $r : $nb;
}
// http://doc.spip.org/@spip_sqlite_insertq
function spip_sqlite_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true){
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
$fields = isset($desc['field']) ? $desc['field'] : array();
foreach ($couples as $champ => $val){
$couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
}
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
$cles = $valeurs = "";
if (count($couples)){
$cles = "(".join(',', array_keys($couples)).")";
$valeurs = "(".join(',', $couples).")";
}
return spip_sqlite_insert($table, $cles, $valeurs, $desc, $serveur, $requeter);
}
// http://doc.spip.org/@spip_sqlite_insertq_multi
function spip_sqlite_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true){
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
if (!isset($desc['field']))
$desc['field'] = array();
// recuperer les champs 'timestamp' pour mise a jour auto de ceux-ci
$maj = _sqlite_ajouter_champs_timestamp($table, array(), $desc, $serveur);
// seul le nom de la table est a traduire ici :
// le faire une seule fois au debut
$query_start = "INSERT INTO $table ";
$query_start = spip_sqlite::traduire_requete($query_start,$serveur);
// ouvrir une transaction
if ($requeter)
spip_sqlite::demarrer_transaction($serveur);
while ($couples = array_shift($tab_couples)){
foreach ($couples as $champ => $val){
$couples[$champ] = _sqlite_calculer_cite($val, $desc['field'][$champ]);
}
// inserer les champs timestamp par defaut
$couples = array_merge($maj,$couples);
$champs = $valeurs = "";
if (count($couples)){
$champs = "(".join(',', array_keys($couples)).")";
$valeurs = "(".join(',', $couples).")";
$query = $query_start."$champs VALUES $valeurs";
}
else
$query = $query_start."DEFAULT VALUES";
if ($requeter)
$retour = spip_sqlite::executer_requete($query,$serveur);
// sur le dernier couple uniquement
if (!count($tab_couples)){
$nb = 0;
if ($requeter)
$nb = spip_sqlite::last_insert_id($serveur);
else
return $query;
}
$err = spip_sqlite_error($query, $serveur);
}
if ($requeter)
spip_sqlite::finir_transaction($serveur);
// renvoie le dernier id d'autoincrement ajoute
// cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
return isset($_GET['var_profile']) ? $retour : $nb;
}
/**
* Retourne si le moteur SQL prefere utiliser des transactions.
*
* @param
* @return bool true / false
**/
function spip_sqlite_preferer_transaction($serveur = '', $requeter = true) {
return true;
}
/**
* Demarre une transaction.
* Pratique pour des sql_updateq() dans un foreach,
* parfois 100* plus rapide s'ils sont nombreux en sqlite !
*
**/
function spip_sqlite_demarrer_transaction($serveur = '', $requeter = true) {
if (!$requeter) return "BEGIN TRANSACTION";
spip_sqlite::demarrer_transaction($serveur);
return true;
}
/**
* Cloture une transaction.
*
**/
function spip_sqlite_terminer_transaction($serveur = '', $requeter = true) {
if (!$requeter) return "COMMIT";
spip_sqlite::finir_transaction($serveur);
return true;
}
// http://doc.spip.org/@spip_sqlite_listdbs
function spip_sqlite_listdbs($serveur = '', $requeter = true){
_sqlite_init();
if (!is_dir($d = substr(_DIR_DB, 0, -1))){
return array();
}
include_spip('inc/flock');
$bases = preg_files($d, $pattern = '(.*)\.sqlite$');
$bds = array();
foreach ($bases as $b){
// pas de bases commencant pas sqlite
// (on s'en sert pour l'installation pour simuler la presence d'un serveur)
// les bases sont de la forme _sqliteX_tmp_spip_install.sqlite
if (strpos($b, '_sqlite')) continue;
$bds[] = preg_replace(";.*/$pattern;iS", '$1', $b);
}
return $bds;
}
// http://doc.spip.org/@spip_sqlite_multi
function spip_sqlite_multi($objet, $lang){
$r = "EXTRAIRE_MULTI(" . $objet . ", '" . $lang . "') AS multi";
return $r;
}
/**
* Optimise une table SQL
* Note: Sqlite optimise TOUTE un fichier sinon rien.
* On evite donc 2 traitements sur la meme base dans un hit.
*
* @param $table nom de la table a optimiser
* @param $serveur nom de la connexion
* @param $requeter effectuer la requete ? sinon retourner son code
* @return bool|string true / false / requete
**/
// http://doc.spip.org/@spip_sqlite_optimize
function spip_sqlite_optimize($table, $serveur = '', $requeter = true){
static $do = false;
if ($requeter and $do){
return true;
}
if ($requeter){
$do = true;
}
return spip_sqlite_query("VACUUM", $serveur, $requeter);
}
//
/**
* echapper une valeur selon son type ou au mieux
* comme le fait _q() mais pour sqlite avec ses specificites
*
* @param string|array|number $v
* @param string $type
* @return string|number
*/
function spip_sqlite_quote($v, $type = ''){
if (!is_array($v))
return _sqlite_calculer_cite($v,$type);
// si c'est un tableau, le parcourir en propageant le type
foreach($v as $k=>$r)
$v[$k] = spip_sqlite_quote($r, $type);
return join(",", $v);
}
/**
* Tester si une date est proche de la valeur d'un champ
*
* @param string $champ le nom du champ a tester
* @param int $interval valeur de l'interval : -1, 4, ...
* @param string $unite utite utilisee (DAY, MONTH, YEAR, ...)
* @return string expression SQL
**/
function spip_sqlite_date_proche($champ, $interval, $unite){
$op = (($interval <= 0) ? '>' : '<');
return "($champ $op datetime('".date("Y-m-d H:i:s")."', '$interval $unite'))";
}
// http://doc.spip.org/@spip_sqlite_replace
function spip_sqlite_replace($table, $couples, $desc = array(), $serveur = '', $requeter = true){
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
$fields = isset($desc['field']) ? $desc['field'] : array();
foreach ($couples as $champ => $val){
$couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
}
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
return spip_sqlite_query("REPLACE INTO $table (".join(',', array_keys($couples)).') VALUES ('.join(',', $couples).')', $serveur);
}
// http://doc.spip.org/@spip_sqlite_replace_multi
function spip_sqlite_replace_multi($table, $tab_couples, $desc = array(), $serveur = '', $requeter = true){
// boucler pour trainter chaque requete independemment
foreach ($tab_couples as $couples){
$retour = spip_sqlite_replace($table, $couples, $desc, $serveur, $requeter);
}
// renvoie le dernier id
return $retour;
}
// http://doc.spip.org/@spip_sqlite_select
function spip_sqlite_select($select, $from, $where = '', $groupby = '', $orderby = '', $limit = '', $having = '', $serveur = '', $requeter = true){
// version() n'est pas connu de sqlite
$select = str_replace('version()', 'sqlite_version()', $select);
// recomposer from
$from = (!is_array($from) ? $from : _sqlite_calculer_select_as($from));
$query =
_sqlite_calculer_expression('SELECT', $select, ', ')
._sqlite_calculer_expression('FROM', $from, ', ')
._sqlite_calculer_expression('WHERE', $where)
._sqlite_calculer_expression('GROUP BY', $groupby, ',')
._sqlite_calculer_expression('HAVING', $having)
.($orderby ? ("\nORDER BY "._sqlite_calculer_order($orderby)) : '')
.($limit ? "\nLIMIT $limit" : '');
// dans un select, on doit renvoyer la requête en cas d'erreur
$res = spip_sqlite_query($query, $serveur, $requeter);
// texte de la requete demande ?
if (!$requeter) return $res;
// erreur survenue ?
if ($res === false) {
return spip_sqlite::traduire_requete($query, $serveur);
}
return $res;
}
/**
* Selectionne un fichier de base de donnees
*
* @param string $nom
* Nom de la base a utiliser
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Inutilise
*
* @return bool|string
* Nom de la base en cas de success.
* False en cas d'erreur.
**/
function spip_sqlite_selectdb($db, $serveur = '', $requeter = true){
_sqlite_init();
// interdire la creation d'une nouvelle base,
// sauf si on est dans l'installation
if (!is_file($f = _DIR_DB.$db.'.sqlite')
&& (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
spip_log("Il est interdit de creer la base $db", 'sqlite.'._LOG_HS);
return false;
}
// se connecter a la base indiquee
// avec les identifiants connus
$index = $serveur ? $serveur : 0;
if ($link = spip_connect_db('', '', '', '', '@selectdb@'.$db, $serveur, '', '')){
if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
return $db;
} else {
spip_log("Impossible de selectionner la base $db", 'sqlite.'._LOG_HS);
return false;
}
}
// http://doc.spip.org/@spip_sqlite_set_charset
function spip_sqlite_set_charset($charset, $serveur = '', $requeter = true){
# spip_log("Gestion charset sql a ecrire : "."SET NAMES "._q($charset), 'sqlite.'._LOG_ERREUR);
# return spip_sqlite_query("SET NAMES ". spip_sqlite_quote($charset), $serveur); //<-- Passe pas !
}
/**
* 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_sqlite_showbase($match, $serveur = '', $requeter = true){
// type est le type d'entrée : table / index / view
// on ne retourne que les tables (?) et non les vues...
# ESCAPE non supporte par les versions sqlite <3
# return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name LIKE "._q($match)." ESCAPE '\'", $serveur, $requeter);
$match = preg_quote($match);
$match = str_replace("\\\_", "[[TIRETBAS]]", $match);
$match = str_replace("\\\%", "[[POURCENT]]", $match);
$match = str_replace("_", ".", $match);
$match = str_replace("%", ".*", $match);
$match = str_replace("[[TIRETBAS]]", "_", $match);
$match = str_replace("[[POURCENT]]", "%", $match);
$match = "^$match$";
return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name REGEXP "._q($match), $serveur, $requeter);
}
define('_SQLITE_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
// http://doc.spip.org/@spip_sqlite_showtable
function spip_sqlite_showtable($nom_table, $serveur = '', $requeter = true){
$query =
'SELECT sql, type FROM'
.' (SELECT * FROM sqlite_master UNION ALL'
.' SELECT * FROM sqlite_temp_master)'
." WHERE tbl_name LIKE '$nom_table'"
." AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'"
.' ORDER BY substr(type,2,1), name';
$a = spip_sqlite_query($query, $serveur, $requeter);
if (!$a) return "";
if (!$requeter) return $a;
if (!($a = spip_sqlite_fetch($a, null, $serveur))) return "";
$vue = ($a['type']=='view'); // table | vue
// c'est une table
// il faut parser le create
if (!$vue){
if (!preg_match(_SQLITE_RE_SHOW_TABLE, array_shift($a), $r)){
return "";
} else {
$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();
$keys = array();
// enlever les contenus des valeurs DEFAULT 'xxx' qui pourraient perturber
// par exemple s'il contiennent une virgule.
// /!\ cela peut aussi echapper le nom des champs si la table a eu des operations avec SQLite Manager !
list($desc, $echaps) = query_echappe_textes($desc);
// separer toutes les descriptions de champs, separes par des virgules
# /!\ explode peut exploser aussi DECIMAL(10,2) !
$k_precedent = null;
foreach (explode(",", $desc) as $v){
preg_match("/^\s*([^\s]+)\s+(.*)/", $v, $r);
// Les cles de champs peuvent etre entourees
// de guillements doubles " , simples ', graves ` ou de crochets [ ], ou rien.
// http://www.sqlite.org/lang_keywords.html
$k = strtolower(query_reinjecte_textes($r[1], $echaps)); // champ, "champ", [champ]...
if ($char = strpbrk($k[0], '\'"[`')) {
$k = trim($k, $char);
if ($char == '[') $k = rtrim($k, ']');
}
$def = query_reinjecte_textes($r[2], $echaps); // valeur du champ
# rustine pour DECIMAL(10,2)
if (false !== strpos($k, ')')) {
$fields[$k_precedent] .= ',' . $k . ' ' . $def;
continue;
}
$fields[$k] = $def;
$k_precedent = $k;
// la primary key peut etre dans une des descriptions de champs
// et non en fin de table, cas encore decouvert avec Sqlite Manager
if (stripos($r[2], 'PRIMARY KEY') !== false) {
$keys['PRIMARY KEY'] = $k;
}
}
// key inclues dans la requete
foreach(preg_split('/\)\s*(,|$)/',$namedkeys) as $v) {
if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/",$v,$r)) {
$k = str_replace("`", '', trim($r[1]));
$t = trim(strtolower(str_replace("`", '', $r[2])), '"');
if ($k && !isset($keys[$k])) $keys[$k] = $t; else $keys[] = $t;
}
}
// sinon ajouter les key index
$query =
'SELECT name,sql FROM'
.' (SELECT * FROM sqlite_master UNION ALL'
.' SELECT * FROM sqlite_temp_master)'
." WHERE tbl_name LIKE '$nom_table'"
." AND type='index' AND name NOT LIKE 'sqlite_%'"
.'ORDER BY substr(type,2,1), name';
$a = spip_sqlite_query($query, $serveur, $requeter);
while ($r = spip_sqlite_fetch($a, null, $serveur)){
$key = str_replace($nom_table.'_', '', $r['name']); // enlever le nom de la table ajoute a l'index
$colonnes = preg_replace(',.*\((.*)\).*,', '$1', $r['sql']);
$keys['KEY '.$key] = $colonnes;
}
}
}
// c'est une vue, on liste les champs disponibles simplement
else {
if ($res = sql_fetsel('*', $nom_table, '', '', '', '1', '', $serveur)){ // limit 1
$fields = array();
foreach ($res as $c => $v) $fields[$c] = '';
$keys = array();
} else {
return "";
}
}
return array('field' => $fields, 'key' => $keys);
}
// http://doc.spip.org/@spip_sqlite_update
function spip_sqlite_update($table, $champs, $where = '', $desc = '', $serveur = '', $requeter = true){
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
$set = array();
foreach ($champs as $champ => $val)
$set[] = $champ."=$val";
if (!empty($set))
return spip_sqlite_query(
_sqlite_calculer_expression('UPDATE', $table, ',')
._sqlite_calculer_expression('SET', $set, ',')
._sqlite_calculer_expression('WHERE', $where),
$serveur, $requeter);
}
// http://doc.spip.org/@spip_sqlite_updateq
function spip_sqlite_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true){
if (!$champs) return;
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
$fields = $desc['field'];
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
$set = array();
foreach ($champs as $champ => $val){
$set[] = $champ.'='._sqlite_calculer_cite($val, $fields[$champ]);
}
return spip_sqlite_query(
_sqlite_calculer_expression('UPDATE', $table, ',')
._sqlite_calculer_expression('SET', $set, ',')
._sqlite_calculer_expression('WHERE', $where),
$serveur, $requeter);
}
/*
*
* Ensuite les fonctions non abstraites
* crees pour l'occasion de sqlite
*
*/
/**
* fonction pour la premiere connexion a un serveur SQLite
* http://doc.spip.org/@_sqlite_init
*
* @return void
*/
function _sqlite_init(){
if (!defined('_DIR_DB')) define('_DIR_DB', _DIR_ETC.'bases/');
if (!defined('_SQLITE_CHMOD')) define('_SQLITE_CHMOD', _SPIP_CHMOD);
if (!is_dir($d = _DIR_DB)){
include_spip('inc/flock');
sous_repertoire($d);
}
}
/**
* teste la version sqlite du link en cours
* http://doc.spip.org/@_sqlite_is_version
*
* @param string $version
* @param string $link
* @param string $serveur
* @param bool $requeter
* @return bool|int
*/
function _sqlite_is_version($version = '', $link = '', $serveur = '', $requeter = true){
if ($link==='') $link = _sqlite_link($serveur);
if (!$link) return false;
if ($link instanceof PDO){
$v = 3;
} else {
$v = 2;
}
if (!$version) return $v;
return ($version==$v);
}
/**
* retrouver un link
* http://doc.spip.org/@_sqlite_link
*
* @param string $serveur
* @return
*/
function _sqlite_link($serveur = ''){
$link = &$GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
return $link;
}
/* ordre alphabetique pour les autres */
/**
* renvoie les bons echappements (pas sur les fonctions now())
* http://doc.spip.org/@_sqlite_calculer_cite
*
* @param string|array|number $v
* @param string $type
* @return string|array|number
*/
function _sqlite_calculer_cite($v, $type){
if ($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))
return $v;
elseif (ctype_xdigit(substr($v, 2)) AND strncmp($v, '0x', 2)==0)
return hexdec(substr($v, 2));
else
return intval($v);
}
}
else {
// si on ne connait pas le type on le deduit de $v autant que possible
if (is_numeric($v))
return strval($v);
}
if (function_exists('sqlite_escape_string')){
return "'".sqlite_escape_string($v)."'";
}
// trouver un link sqlite3 pour faire l'echappement
foreach ($GLOBALS['connexions'] as $s){
if (_sqlite_is_version(3, $l = $s['link'])){
return $l->quote($v);
}
}
// echapper les ' en ''
spip_log("Pas de methode sqlite_escape_string ni ->quote pour echapper","sqlite."._LOG_INFO_IMPORTANTE);
return ("'" . str_replace("'","''",$v) . "'");
}
/**
* renvoie grosso modo "$expression join($join, $v)"
* http://doc.spip.org/@_sqlite_calculer_expression
*
* @param $expression
* @param $v
* @param string $join
* @return string
*/
function _sqlite_calculer_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('_sqlite_calculer_where', $v));
else
return $exp.join($join, $v);
}
}
/**
* pour conversion 0+x ? (pas la peine en sqlite)
* http://doc.spip.org/@_sqlite_calculer_order
*
* @param $orderby
* @return string
*/
function _sqlite_calculer_order($orderby){
return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
}
// renvoie des 'nom AS alias'
// http://doc.spip.org/@_sqlite_calculer_select_as
function _sqlite_calculer_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);
}
/**
* renvoie les bonnes parentheses pour des where imbriquees
* http://doc.spip.org/@_sqlite_calculer_where
*
* @param $v
* @return array|mixed|string
*/
function _sqlite_calculer_where($v){
if (!is_array($v))
return $v;
$op = array_shift($v);
if (!($n = count($v)))
return $op;
else {
$arg = _sqlite_calculer_where(array_shift($v));
if ($n==1){
return "$op($arg)";
} else {
$arg2 = _sqlite_calculer_where(array_shift($v));
if ($n==2){
return "($arg $op $arg2)";
} else return "($arg $op ($arg2) : $v[0])";
}
}
}
/**
* Charger les modules sqlite (si possible) (juste la version demandee),
* ou, si aucune version, renvoie les versions sqlite dispo
* sur ce serveur dans un array
*
* http://doc.spip.org/@_sqlite_charger_version
*
* @param string $version
* @return array|bool
*/
function _sqlite_charger_version($version = ''){
$versions = array();
// version 2
if (!$version || $version==2){
if (charger_php_extension('sqlite')){
$versions[] = 2;
}
}
// version 3
if (!$version || $version==3){
if (charger_php_extension('pdo') && charger_php_extension('pdo_sqlite')){
$versions[] = 3;
}
}
if ($version) return in_array($version, $versions);
return $versions;
}
/**
* Gestion des requetes ALTER non reconnues de SQLite :
* ALTER TABLE table DROP column
* ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
* ALTER TABLE table MODIFY column definition
* ALTER TABLE table ADD|DROP PRIMARY KEY
*
* (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
*
* 1) creer une table B avec le nouveau format souhaite
* 2) copier la table d'origine A vers B
* 3) supprimer la table A
* 4) renommer la table B en A
* 5) remettre les index (qui sont supprimes avec la table A)
*
* http://doc.spip.org/@_sqlite_modifier_table
*
* @param string/array $table : nom_table, array(nom_table=>nom_futur)
* @param string/array $col : nom_colonne, array(nom_colonne=>nom_futur)
* @param array $opt : options comme les tables spip, qui sera merge a la table creee : array('field'=>array('nom'=>'syntaxe', ...), 'key'=>array('KEY nom'=>'colonne', ...))
* @param string $serveur : nom de la connexion sql en cours
*
*/
function _sqlite_modifier_table($table, $colonne, $opt = array(), $serveur = ''){
if (is_array($table)){
reset($table);
list($table_origine,$table_destination) = each($table);
} else {
$table_origine = $table_destination = $table;
}
// ne prend actuellement qu'un changement
// mais pourra etre adapte pour changer plus qu'une colonne a la fois
if (is_array($colonne)){
reset($colonne);
list($colonne_origine,$colonne_destination) = each($colonne);
} else {
$colonne_origine = $colonne_destination = $colonne;
}
if (!isset($opt['field'])) $opt['field'] = array();
if (!isset($opt['key'])) $opt['key'] = array();
// si les noms de tables sont differents, pas besoin de table temporaire
// on prendra directement le nom de la future table
$meme_table = ($table_origine==$table_destination);
$def_origine = sql_showtable($table_origine, false, $serveur);
if (!$def_origine OR !isset($def_origine['field'])){
spip_log("Alter table impossible sur $table_origine : table non trouvee",'sqlite'._LOG_ERREUR);
return false;
}
$table_tmp = $table_origine.'_tmp';
// 1) creer une table temporaire avec les modifications
// - DROP : suppression de la colonne
// - CHANGE : modification de la colonne
// (foreach pour conserver l'ordre des champs)
// field
$fields = array();
// pour le INSERT INTO plus loin
// stocker la correspondance nouvelles->anciennes colonnes
$fields_correspondances = array();
foreach ($def_origine['field'] as $c => $d){
if ($colonne_origine && ($c==$colonne_origine)){
// si pas DROP
if ($colonne_destination){
$fields[$colonne_destination] = $opt['field'][$colonne_destination];
$fields_correspondances[$colonne_destination] = $c;
}
} else {
$fields[$c] = $d;
$fields_correspondances[$c] = $c;
}
}
// cas de ADD sqlite2 (ajout du champ en fin de table):
if (!$colonne_origine && $colonne_destination){
$fields[$colonne_destination] = $opt['field'][$colonne_destination];
}
// key...
$keys = array();
foreach ($def_origine['key'] as $c => $d){
$c = str_replace($colonne_origine, $colonne_destination, $c);
$d = str_replace($colonne_origine, $colonne_destination, $d);
// seulement si on ne supprime pas la colonne !
if ($d)
$keys[$c] = $d;
}
// autres keys, on merge
$keys = array_merge($keys, $opt['key']);
$queries = array();
// copier dans destination (si differente de origine), sinon tmp
$table_copie = ($meme_table) ? $table_tmp : $table_destination;
$autoinc = (isset($keys['PRIMARY KEY'])
AND stripos($keys['PRIMARY KEY'],',')===false
AND stripos($fields[$keys['PRIMARY KEY']],'default')===false);
if ($q = _sqlite_requete_create(
$table_copie,
$fields,
$keys,
$autoinc,
$temporary = false,
$ifnotexists = true,
$serveur)){
$queries[] = $q;
}
// 2) y copier les champs qui vont bien
$champs_dest = join(', ', array_keys($fields_correspondances));
$champs_ori = join(', ', $fields_correspondances);
$queries[] = "INSERT INTO $table_copie ($champs_dest) SELECT $champs_ori FROM $table_origine";
// 3) supprimer la table d'origine
$queries[] = "DROP TABLE $table_origine";
// 4) renommer la table temporaire
// avec le nom de la table destination
// si necessaire
if ($meme_table){
if (_sqlite_is_version(3, '', $serveur)){
$queries[] = "ALTER TABLE $table_copie RENAME TO $table_destination";
} else {
$queries[] = _sqlite_requete_create(
$table_destination,
$fields,
$keys,
$autoinc,
$temporary = false,
$ifnotexists = false, // la table existe puisqu'on est dans une transaction
$serveur);
$queries[] = "INSERT INTO $table_destination SELECT * FROM $table_copie";
$queries[] = "DROP TABLE $table_copie";
}
}
// 5) remettre les index !
foreach ($keys as $k => $v){
if ($k=='PRIMARY KEY'){
}
else {
// enlever KEY
$k = substr($k, 4);
$queries[] = "CREATE INDEX $table_destination"."_$k ON $table_destination ($v)";
}
}
if (count($queries)){
spip_sqlite::demarrer_transaction($serveur);
// il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas
foreach ($queries as $q){
if (!spip_sqlite::executer_requete($q, $serveur)){
spip_log(_LOG_GRAVITE_ERREUR, "SQLite : ALTER TABLE table :"
." Erreur a l'execution de la requete : $q", 'sqlite');
spip_sqlite::annuler_transaction($serveur);
return false;
}
}
spip_sqlite::finir_transaction($serveur);
}
return true;
}
/**
* Nom des fonctions
* http://doc.spip.org/@_sqlite_ref_fonctions
*
* @return array
*/
function _sqlite_ref_fonctions(){
$fonctions = array(
'alter' => 'spip_sqlite_alter',
'count' => 'spip_sqlite_count',
'countsel' => 'spip_sqlite_countsel',
'create' => 'spip_sqlite_create',
'create_base' => 'spip_sqlite_create_base',
'create_view' => 'spip_sqlite_create_view',
'date_proche' => 'spip_sqlite_date_proche',
'delete' => 'spip_sqlite_delete',
'drop_table' => 'spip_sqlite_drop_table',
'drop_view' => 'spip_sqlite_drop_view',
'errno' => 'spip_sqlite_errno',
'error' => 'spip_sqlite_error',
'explain' => 'spip_sqlite_explain',
'fetch' => 'spip_sqlite_fetch',
'seek' => 'spip_sqlite_seek',
'free' => 'spip_sqlite_free',
'hex' => 'spip_sqlite_hex',
'in' => 'spip_sqlite_in',
'insert' => 'spip_sqlite_insert',
'insertq' => 'spip_sqlite_insertq',
'insertq_multi' => 'spip_sqlite_insertq_multi',
'listdbs' => 'spip_sqlite_listdbs',
'multi' => 'spip_sqlite_multi',
'optimize' => 'spip_sqlite_optimize',
'query' => 'spip_sqlite_query',
'quote' => 'spip_sqlite_quote',
'replace' => 'spip_sqlite_replace',
'replace_multi' => 'spip_sqlite_replace_multi',
'select' => 'spip_sqlite_select',
'selectdb' => 'spip_sqlite_selectdb',
'set_charset' => 'spip_sqlite_set_charset',
'get_charset' => 'spip_sqlite_get_charset',
'showbase' => 'spip_sqlite_showbase',
'showtable' => 'spip_sqlite_showtable',
'update' => 'spip_sqlite_update',
'updateq' => 'spip_sqlite_updateq',
'preferer_transaction' => 'spip_sqlite_preferer_transaction',
'demarrer_transaction' => 'spip_sqlite_demarrer_transaction',
'terminer_transaction' => 'spip_sqlite_terminer_transaction',
);
// association de chaque nom http d'un charset aux couples sqlite
// SQLite supporte utf-8 et utf-16 uniquement.
$charsets = array(
'utf-8' => array('charset' => 'utf8', 'collation' => 'utf8_general_ci'),
//'utf-16be'=>array('charset'=>'utf16be','collation'=>'UTF-16BE'),// aucune idee de quoi il faut remplir dans es champs la
//'utf-16le'=>array('charset'=>'utf16le','collation'=>'UTF-16LE')
);
$fonctions['charsets'] = $charsets;
return $fonctions;
}
/**
* $query est une requete ou une liste de champs
* http://doc.spip.org/@_sqlite_remplacements_definitions_table
*
* @param $query
* @param bool $autoinc
* @return mixed
*/
function _sqlite_remplacements_definitions_table($query, $autoinc = false){
// quelques remplacements
$num = "(\s*\([0-9]*\))?";
$enum = "(\s*\([^\)]*\))?";
$remplace = array(
'/enum'.$enum.'/is' => 'VARCHAR(255)',
'/COLLATE \w+_bin/is' => 'COLLATE BINARY',
'/COLLATE \w+_ci/is' => 'COLLATE NOCASE',
'/auto_increment/is' => '',
'/(timestamp .* )ON .*$/is' => '\\1',
'/character set \w+/is' => '',
'/((big|small|medium|tiny)?int(eger)?)'.$num.'\s*unsigned/is' => '\\1 UNSIGNED',
'/(text\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
'/((char|varchar)'.$num.'\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
'/(datetime\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00 00:00:00'",
'/(date\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00'",
);
// pour l'autoincrement, il faut des INTEGER NOT NULL PRIMARY KEY
$remplace_autocinc = array(
'/(big|small|medium|tiny)?int(eger)?'.$num.'/is' => 'INTEGER'
);
// pour les int non autoincrement, il faut un DEFAULT
$remplace_nonautocinc = array(
'/((big|small|medium|tiny)?int(eger)?'.$num.'\s+not\s+null)\s*$/is' => "\\1 DEFAULT 0",
);
if (is_string($query)){
$query = preg_replace(array_keys($remplace), $remplace, $query);
if ($autoinc OR preg_match(',AUTO_INCREMENT,is',$query))
$query = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query);
else{
$query = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query);
$query = _sqlite_collate_ci($query);
}
}
elseif(is_array($query)){
foreach($query as $k=>$q) {
$ai = ($autoinc?$k==$autoinc:preg_match(',AUTO_INCREMENT,is',$q));
$query[$k] = preg_replace(array_keys($remplace), $remplace, $query[$k]);
if ($ai)
$query[$k] = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query[$k]);
else{
$query[$k] = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query[$k]);
$query[$k] = _sqlite_collate_ci($query[$k]);
}
}
}
return $query;
}
/**
* Definir la collation d'un champ en fonction de si une collation est deja explicite
* et du par defaut que l'on veut NOCASE
* @param string $champ
* @return string
*/
function _sqlite_collate_ci($champ){
if (stripos($champ,"COLLATE")!==false)
return $champ;
if (stripos($champ,"BINARY")!==false)
return str_ireplace("BINARY","COLLATE BINARY",$champ);
if (preg_match(",^(char|varchar|(long|small|medium|tiny)?text),i",$champ))
return $champ . " COLLATE NOCASE";
return $champ;
}
/**
* Creer la requete pour la creation d'une table
* retourne la requete pour utilisation par sql_create() et sql_alter()
*
* http://doc.spip.org/@_sqlite_requete_create
*
* @param $nom
* @param $champs
* @param $cles
* @param bool $autoinc
* @param bool $temporary
* @param bool $_ifnotexists
* @param string $serveur
* @param bool $requeter
* @return bool|string
*/
function _sqlite_requete_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $_ifnotexists = true, $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;
// sqlite ne gere pas KEY tout court dans une requete CREATE TABLE
// il faut passer par des create index
// Il gere par contre primary key !
// Soit la PK est definie dans les cles, soit dans un champs
$c = ""; // le champ de cle primaire
if (!isset($cles[$pk = "PRIMARY KEY"]) OR !$c = $cles[$pk]){
foreach ($champs as $k => $v){
if (false!==stripos($v, $pk)){
$c = $k;
// on n'en a plus besoin dans field, vu que defini dans key
$champs[$k] = preg_replace("/$pk/is", '', $champs[$k]);
break;
}
}
}
if ($c) $keys = "\n\t\t$pk ($c)";
// Pas de DEFAULT 0 sur les cles primaires en auto-increment
if (isset($champs[$c])
AND stripos($champs[$c],"default 0")!==false){
$champs[$c] = trim(str_ireplace("default 0","",$champs[$c]));
}
$champs = _sqlite_remplacements_definitions_table($champs, $autoinc?$c:false);
foreach ($champs as $k => $v){
$query .= "$s\n\t\t$k $v";
$s = ",";
}
$ifnotexists = "";
if ($_ifnotexists){
$version = spip_sqlite_fetch(spip_sqlite_query("select sqlite_version() AS sqlite_version",$serveur),'',$serveur);
if (!function_exists('spip_version_compare')) include_spip('plugins/installer');
if ($version AND spip_version_compare($version['sqlite_version'],'3.3.0','>=')) {
$ifnotexists = ' IF NOT EXISTS';
} else {
/* simuler le IF EXISTS - version 2 et sqlite < 3.3a */
$a = spip_sqlite_showtable($table, $serveur);
if (isset($a['key']['KEY '.$nom])) return true;
}
}
$temporary = $temporary ? ' TEMPORARY' : '';
$q = "CREATE$temporary TABLE$ifnotexists $nom ($query".($keys ? ",$keys" : '').")\n";
return $q;
}
/**
* Retrouver les champs 'timestamp'
* pour les ajouter aux 'insert' ou 'replace'
* afin de simuler le fonctionnement de mysql
*
* stocke le resultat pour ne pas faire
* de requetes showtable intempestives
*
* http://doc.spip.org/@_sqlite_ajouter_champs_timestamp
*
* @param $table
* @param $couples
* @param string $desc
* @param string $serveur
* @return
*/
function _sqlite_ajouter_champs_timestamp($table, $couples, $desc = '', $serveur = ''){
static $tables = array();
if (!isset($tables[$table])){
if (!$desc){
$trouver_table = charger_fonction('trouver_table', 'base');
$desc = $trouver_table($table, $serveur);
// si pas de description, on ne fait rien, ou on die() ?
if (!$desc) return $couples;
}
// recherche des champs avec simplement 'TIMESTAMP'
// cependant, il faudra peut etre etendre
// avec la gestion de DEFAULT et ON UPDATE
// mais ceux-ci ne sont pas utilises dans le core
$tables[$table] = array();
foreach ($desc['field'] as $k => $v){
if (strpos(strtolower(ltrim($v)), 'timestamp')===0)
$tables[$table][$k] = "datetime('now')";
}
}
// ajout des champs type 'timestamp' absents
return array_merge($tables[$table],$couples);
}
/**
* renvoyer la liste des versions sqlite disponibles
* sur le serveur
* http://doc.spip.org/@spip_versions_sqlite
*
* @return array|bool
*/
function spip_versions_sqlite(){
return _sqlite_charger_version();
}
class spip_sqlite {
static $requeteurs = array();
static $transaction_en_cours = array();
function spip_sqlite(){}
/**
* Retourne une unique instance du requêteur
*
* Retourne une instance unique du requêteur pour une connexion SQLite
* donnée
*
* @param string $serveur
* Nom du connecteur
* @return sqlite_requeteur
* Instance unique du requêteur
**/
static function requeteur($serveur){
if (!isset(spip_sqlite::$requeteurs[$serveur]))
spip_sqlite::$requeteurs[$serveur] = new sqlite_requeteur($serveur);
return spip_sqlite::$requeteurs[$serveur];
}
static function traduire_requete($query, $serveur){
$requeteur = spip_sqlite::requeteur($serveur);
$traducteur = new sqlite_traducteur($query, $requeteur->prefixe,$requeteur->sqlite_version);
return $traducteur->traduire_requete();
}
static function demarrer_transaction($serveur){
spip_sqlite::executer_requete("BEGIN TRANSACTION",$serveur);
spip_sqlite::$transaction_en_cours[$serveur] = true;
}
static function executer_requete($query, $serveur, $tracer=null){
$requeteur = spip_sqlite::requeteur($serveur);
return $requeteur->executer_requete($query, $tracer);
}
static function last_insert_id($serveur){
$requeteur = spip_sqlite::requeteur($serveur);
return $requeteur->last_insert_id($serveur);
}
static function annuler_transaction($serveur){
spip_sqlite::executer_requete("ROLLBACK",$serveur);
spip_sqlite::$transaction_en_cours[$serveur] = false;
}
static function finir_transaction($serveur){
// si pas de transaction en cours, ne rien faire et le dire
if (!isset (spip_sqlite::$transaction_en_cours[$serveur])
OR spip_sqlite::$transaction_en_cours[$serveur]==false)
return false;
// sinon fermer la transaction et retourner true
spip_sqlite::executer_requete("COMMIT",$serveur);
spip_sqlite::$transaction_en_cours[$serveur] = false;
return true;
}
}
/*
* Classe pour partager les lancements de requete
* instanciee une fois par $serveur
* - peut corriger la syntaxe des requetes pour la conformite a sqlite
* - peut tracer les requetes
*
*/
class sqlite_requeteur {
var $query = ''; // la requete
var $serveur = ''; // le serveur
var $link = ''; // le link (ressource) sqlite
var $prefixe = ''; // le prefixe des tables
var $db = ''; // le nom de la base
var $tracer = false; // doit-on tracer les requetes (var_profile)
var $sqlite_version = ''; // Version de sqlite (2 ou 3)
/**
* constructeur
* http://doc.spip.org/@sqlite_traiter_requete
*
* @param $query
* @param string $serveur
* @return bool
*/
function sqlite_requeteur($serveur = ''){
_sqlite_init();
$this->serveur = strtolower($serveur);
if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
spip_log("Aucune connexion sqlite (link)", 'sqlite.'._LOG_ERREUR);
return false;
}
$this->sqlite_version = _sqlite_is_version('', $this->link);
$this->prefixe = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['prefixe'];
$this->db = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['db'];
// tracage des requetes ?
$this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']);
}
/**
* lancer la requete $query,
* faire le tracage si demande
* http://doc.spip.org/@executer_requete
*
* @return bool|SQLiteResult
*/
function executer_requete($query, $tracer=null){
if (is_null($tracer))
$tracer = $this->tracer;
$err = "";
$t = 0;
if ($tracer){
include_spip('public/tracer');
$t = trace_query_start();
}
# spip_log("requete: $this->serveur >> $query",'sqlite.'._LOG_DEBUG); // boum ? pourquoi ?
if ($this->link){
// memoriser la derniere erreur PHP vue
$e = (function_exists('error_get_last')?error_get_last():"");
// sauver la derniere requete
$GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['last'] = $query;
if ($this->sqlite_version==3){
$r = $this->link->query($query);
// sauvegarde de la requete (elle y est deja dans $r->queryString)
# $r->spipQueryString = $query;
// comptage : oblige de compter le nombre d'entrees retournees
// par une requete SELECT
// aucune autre solution ne donne le nombre attendu :( !
// particulierement s'il y a des LIMIT dans la requete.
if (strtoupper(substr(ltrim($query), 0, 6))=='SELECT'){
if ($r){
// noter le link et la query pour faire le comptage *si* on en a besoin
$r->spipSqliteRowCount = array($this->link,$query);
}
elseif ($r instanceof PDOStatement) {
$r->spipSqliteRowCount = 0;
}
}
}
else {
$r = sqlite_query($this->link, $query);
}
// loger les warnings/erreurs eventuels de sqlite remontant dans PHP
if ($err = (function_exists('error_get_last')?error_get_last():"") AND $err!=$e){
$err = strip_tags($err['message'])." in ".$err['file']." line ".$err['line'];
spip_log("$err - ".$query, 'sqlite.'._LOG_ERREUR);
}
else $err = "";
}
else {
$r = false;
}
if (spip_sqlite_errno($this->serveur))
$err .= spip_sqlite_error($query, $this->serveur);
return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r;
}
function last_insert_id(){
if ($this->sqlite_version==3)
return $this->link->lastInsertId();
else
return sqlite_last_insert_rowid($this->link);
}
}
/**
* Cette classe est presente essentiellement pour un preg_replace_callback
* avec des parametres dans la fonction appelee que l'on souhaite incrementer
* (fonction pour proteger les textes)
*/
class sqlite_traducteur {
var $query = '';
var $prefixe = ''; // le prefixe des tables
var $sqlite_version = ''; // Version de sqlite (2 ou 3)
// Pour les corrections a effectuer sur les requetes :
var $textes = array(); // array(code=>'texte') trouvé
function sqlite_traducteur($query, $prefixe, $sqlite_version){
$this->query = $query;
$this->prefixe = $prefixe;
$this->sqlite_version = $sqlite_version;
}
/**
* transformer la requete pour sqlite
* enleve les textes, transforme la requete pour quelle soit
* bien interpretee par sqlite, puis remet les textes
* la fonction affecte $this->query
* http://doc.spip.org/@traduire_requete
*
* @return void
*/
function traduire_requete(){
//
// 1) Protection des textes en les remplacant par des codes
//
// enlever les 'textes' et initialiser avec
list($this->query, $textes) = query_echappe_textes($this->query);
//
// 2) Corrections de la requete
//
// Correction Create Database
// Create Database -> requete ignoree
if (strpos($this->query, 'CREATE DATABASE')===0){
spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.'._LOG_AVERTISSEMENT);
$this->query = "SELECT 1";
}
// Correction Insert Ignore
// INSERT IGNORE -> insert (tout court et pas 'insert or replace')
if (strpos($this->query, 'INSERT IGNORE')===0){
spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.'._LOG_DEBUG);
$this->query = 'INSERT '.substr($this->query, '13');
}
// Correction des dates avec INTERVAL
// utiliser sql_date_proche() de preference
if (strpos($this->query, 'INTERVAL')!==false){
$this->query = preg_replace_callback("/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U",
array(&$this, '_remplacerDateParTime'),
$this->query);
}
if (strpos($this->query, 'LEFT(')!==false){
$this->query = str_replace('LEFT(','_LEFT(',$this->query);
}
if (strpos($this->query, 'TIMESTAMPDIFF(')!==false){
$this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims',"TIMESTAMPDIFF('\\1',",$this->query);
}
// Correction Using
// USING (non reconnu en sqlite2)
// problematique car la jointure ne se fait pas du coup.
if (($this->sqlite_version==2) && (strpos($this->query, "USING")!==false)){
spip_log("'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", 'sqlite.'._LOG_ERREUR);
$this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query);
}
// Correction Field
// remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
if (strpos($this->query, 'FIELD')!==false){
$this->query = preg_replace_callback('/FIELD\s*\(([^\)]*)\)/',
array(&$this, '_remplacerFieldParCase'),
$this->query);
}
// Correction des noms de tables FROM
// mettre les bons noms de table dans from, update, insert, replace...
if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', $this->query, $regs)){
$suite = strstr($this->query, $regs[0]);
$this->query = substr($this->query, 0, -strlen($suite));
}
else
$suite = '';
$pref = ($this->prefixe) ? $this->prefixe."_" : "";
$this->query = preg_replace('/([,\s])spip_/S', '\1'.$pref, $this->query).$suite;
// Correction zero AS x
// pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
// n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
// il dit que x ne doit pas être un integer dans le order by !
// on remplace du coup x par vide() dans ce cas uniquement
//
// rien que pour public/vertebrer.php ?
if ((strpos($this->query, "0 AS")!==false)){
// on ne remplace que dans ORDER BY ou GROUP BY
if (preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)){
$suite = strstr($this->query, $regs[0]);
$this->query = substr($this->query, 0, -strlen($suite));
// on cherche les noms des x dans 0 AS x
// on remplace dans $suite le nom par vide()
preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER);
foreach ($matches[1] as $m){
$suite = str_replace($m, 'VIDE()', $suite);
}
$this->query .= $suite;
}
}
// Correction possible des divisions entieres
// Le standard SQL (lequel? ou?) semble indiquer que
// a/b=c doit donner c entier si a et b sont entiers 4/3=1.
// C'est ce que retournent effectivement SQL Server et SQLite
// Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
//
// On peut forcer la conversion en multipliant par 1.0 avant la division
// /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus !
// cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
// http://www.sqlite.org/cvstrac/tktview?tn=3202
// (4*1.0/3) n'est pas rendu dans ce cas !
# $this->query = str_replace('/','* 1.00 / ',$this->query);
// Correction critere REGEXP, non reconnu en sqlite2
if (($this->sqlite_version==2) && (strpos($this->query, 'REGEXP')!==false)){
$this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query);
}
//
// 3) Remise en place des textes d'origine
//
// Correction Antiquotes et echappements
// ` => rien
if (strpos($this->query,'`')!==false)
$this->query = str_replace('`','', $this->query);
$this->query = query_reinjecte_textes($this->query, $textes);
return $this->query;
}
/**
* les callbacks
* remplacer DATE_ / INTERVAL par DATE...strtotime
* http://doc.spip.org/@_remplacerDateParTime
*
* @param $matches
* @return string
*/
function _remplacerDateParTime($matches){
$op = strtoupper($matches[1]=='ADD') ? '+' : '-';
return "datetime$matches[2] '$op$matches[3] $matches[4]')";
}
/**
* callback ou l'on remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
* http://doc.spip.org/@_remplacerFieldParCase
*
* @param $matches
* @return string
*/
function _remplacerFieldParCase($matches){
$fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
$t = explode(',', $fields);
$index = array_shift($t);
$res = '';
$n = 0;
foreach ($t as $v){
$n++;
$res .= "\nWHEN $index=$v THEN $n";
}
return "CASE $res ELSE 0 END ";
}
}
?>
sqlite3.php 0000666 00000002630 15250277754 0006664 0 ustar 00 <?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2014 *
* 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. *
\***************************************************************************/
if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('req/sqlite_generique');
$GLOBALS['spip_sqlite3_functions_1'] = _sqlite_ref_fonctions();
// http://doc.spip.org/@req_sqlite3_dist
function req_sqlite3_dist($addr, $port, $login, $pass, $db='', $prefixe='') {
return req_sqlite_dist($addr, $port, $login, $pass, $db, $prefixe, $sqlite_version=3);
}
// http://doc.spip.org/@spip_sqlite3_constantes
function spip_sqlite3_constantes(){
if (!defined('SPIP_SQLITE3_ASSOC')) {
define('SPIP_SQLITE3_ASSOC', PDO::FETCH_ASSOC);
define('SPIP_SQLITE3_NUM', PDO::FETCH_NUM);
define('SPIP_SQLITE3_BOTH', PDO::FETCH_BOTH);
}
}
function spip_versions_sqlite3()
{
return _sqlite_charger_version(3) ? 3 : false;
}
?>
pg.php 0000666 00000130022 15250277754 0005703 0 ustar 00 <?php
/* *************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2014 *
* 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 gerant
* les instructions SQL pour PostgreSQL
*
* @package SPIP\SQL\PostgreSQL
*/
if (!defined('_ECRIRE_INC_VERSION')) return;
define('_DEFAULT_DB', 'spip');
// Se connecte et retourne le nom de la fonction a connexion persistante
// A la premiere connexion de l'installation (BD pas precisee)
// si on ne peut se connecter sans la preciser
// on reessaye avec le login comme nom de BD
// et si ca marche toujours pas, avec "spip" (constante ci-dessus)
// si ca ne marche toujours pas, echec.
// http://doc.spip.org/@req_pg_dist
function req_pg_dist($addr, $port, $login, $pass, $db='', $prefixe='') {
static $last_connect = array();
if (!charger_php_extension('pgsql')) return false;
// si provient de selectdb
if (empty($addr) && empty($port) && empty($login) && empty($pass)){
foreach (array('addr','port','login','pass','prefixe') as $a){
$$a = $last_connect[$a];
}
}
@list($host, $p) = explode(';', $addr);
if ($p >0) $port = " port=$p" ; else $port = '';
$erreurs = array();
if ($db) {
@$link = pg_connect("host=$host$port dbname=$db user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW);
} elseif (!@$link = pg_connect("host=$host$port user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW)) {
$erreurs[] = pg_last_error();
if (@$link = pg_connect("host=$host$port dbname=$login user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW)) {
$db = $login;
} else {
$erreurs[] = pg_last_error();
$db = _DEFAULT_DB;
$link = pg_connect("host=$host$port dbname=$db user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW);
}
}
if (!$link) {
$erreurs[] = pg_last_error();
foreach($erreurs as $e)
spip_log('Echec pg_connect. Erreur : ' . $e,'pg.'._LOG_HS);
return false;
}
if ($link)
$last_connect = array (
'addr' => $addr,
'port' => $port,
'login' => $login,
'pass' => $pass,
'db' => $db,
'prefixe' => $prefixe,
);
spip_log("Connexion vers $host, base $db, prefixe $prefixe " . ($link ? 'operationnelle' : 'impossible'),'pg.'._LOG_DEBUG);
return !$link ? false : array(
'db' => $db,
'prefixe' => $prefixe ? $prefixe : $db,
'link' => $link,
);
}
$GLOBALS['spip_pg_functions_1'] = array(
'alter' => 'spip_pg_alter',
'count' => 'spip_pg_count',
'countsel' => 'spip_pg_countsel',
'create' => 'spip_pg_create',
'create_base' => 'spip_pg_create_base',
'create_view' => 'spip_pg_create_view',
'date_proche' => 'spip_pg_date_proche',
'delete' => 'spip_pg_delete',
'drop_table' => 'spip_pg_drop_table',
'drop_view' => 'spip_pg_drop_view',
'errno' => 'spip_pg_errno',
'error' => 'spip_pg_error',
'explain' => 'spip_pg_explain',
'fetch' => 'spip_pg_fetch',
'seek' => 'spip_pg_seek',
'free' => 'spip_pg_free',
'hex' => 'spip_pg_hex',
'in' => 'spip_pg_in',
'insert' => 'spip_pg_insert',
'insertq' => 'spip_pg_insertq',
'insertq_multi' => 'spip_pg_insertq_multi',
'listdbs' => 'spip_pg_listdbs',
'multi' => 'spip_pg_multi',
'optimize' => 'spip_pg_optimize',
'query' => 'spip_pg_query',
'quote' => 'spip_pg_quote',
'replace' => 'spip_pg_replace',
'replace_multi' => 'spip_pg_replace_multi',
'select' => 'spip_pg_select',
'selectdb' => 'spip_pg_selectdb',
'set_connect_charset' => 'spip_pg_set_connect_charset',
'showbase' => 'spip_pg_showbase',
'showtable' => 'spip_pg_showtable',
'update' => 'spip_pg_update',
'updateq' => 'spip_pg_updateq',
);
// Par ou ca passe une fois les traductions faites
// http://doc.spip.org/@spip_pg_trace_query
function spip_pg_trace_query($query, $serveur='')
{
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if (isset($_GET['var_profile'])) {
include_spip('public/tracer');
$t = trace_query_start();
} else $t = 0 ;
$connexion['last'] = $query;
$r = spip_pg_query_simple($link, $query);
if ($e = spip_pg_errno($serveur)) // Log de l'erreur eventuelle
$e .= spip_pg_error($query, $serveur); // et du fautif
return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
}
// Fonction de requete generale quand on est sur que c'est SQL standard.
// Elle change juste le noms des tables ($table_prefix) dans le FROM etc
// http://doc.spip.org/@spip_pg_query
function spip_pg_query($query, $serveur='',$requeter=true)
{
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
$suite = strstr($query, $regs[0]);
$query = substr($query, 0, -strlen($suite));
} else $suite ='';
$query = preg_replace('/([,\s])spip_/', '\1'.$prefixe.'_', $query) . $suite;
// renvoyer la requete inerte si demandee
if (!$requeter) return $query;
return spip_pg_trace_query($query, $serveur);
}
function spip_pg_query_simple($link, $query){
#spip_log(var_export($query,true), 'pg.'._LOG_DEBUG);
return pg_query($link, $query);
}
/*
* Retrouver les champs 'timestamp'
* pour les ajouter aux 'insert' ou 'replace'
* afin de simuler le fonctionnement de mysql
*
* stocke le resultat pour ne pas faire
* de requetes showtable intempestives
*/
function spip_pg_ajouter_champs_timestamp($table, $couples, $desc='', $serveur=''){
static $tables = array();
if (!isset($tables[$table])){
if (!$desc){
$trouver_table = charger_fonction('trouver_table', 'base');
$desc = $trouver_table($table, $serveur);
// si pas de description, on ne fait rien, ou on die() ?
if (!$desc) return $couples;
}
// recherche des champs avec simplement 'TIMESTAMP'
// cependant, il faudra peut etre etendre
// avec la gestion de DEFAULT et ON UPDATE
// mais ceux-ci ne sont pas utilises dans le core
$tables[$table] = array();
foreach ($desc['field'] as $k=>$v){
$v = strtolower(ltrim($v));
// ne pas ajouter de timestamp now() si un default est specifie
if (strpos($v, 'timestamp')===0 AND strpos($v, 'default')===false)
$tables[$table][] = $k;
}
}
// ajout des champs type 'timestamp' absents
foreach ($tables[$table] as $maj){
if (!array_key_exists($maj, $couples))
$couples[$maj] = "NOW()";
}
return $couples;
}
// Alter en PG ne traite pas les index
// http://doc.spip.org/@spip_pg_alter
function spip_pg_alter($query, $serveur='',$requeter=true) {
// il faudrait une regexp pour eviter de spliter ADD PRIMARY KEY (colA, colB)
// tout en cassant en deux alter distincts "ADD PRIMARY KEY (colA, colB), ADD INDEX (chose)"...
// ou revoir l'api de sql_alter en creant un
// sql_alter_table($table,array($actions));
if (!preg_match("/\s*((\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
spip_log("$query mal comprise", 'pg.'._LOG_ERREUR);
return false;
}
$debut = $regs[1];
$table = $regs[3];
$suite = $regs[4];
$todo = explode(',', $suite);
// on remet les morceaux dechires ensembles... que c'est laid !
$todo2 = array(); $i=0;
$ouverte = false;
while ($do = array_shift($todo)) {
$todo2[$i] = isset($todo2[$i]) ? $todo2[$i] . "," . $do : $do;
$o=(false!==strpos($do,"("));
$f=(false!==strpos($do,")"));
if ($o AND !$f) $ouverte=true;
elseif ($f) $ouverte=false;
if (!$ouverte) $i++;
}
$todo=$todo2;
$query = $debut.' '.array_shift($todo);
if (!preg_match('/^\s*(IGNORE\s*)?TABLE\s+(\w+)\s+(ADD|DROP|CHANGE|MODIFY|RENAME)\s*(.*)$/is', $query, $r)) {
spip_log("$query incompris", 'pg.'._LOG_ERREUR);
} else {
if ($r[1]) spip_log("j'ignore IGNORE dans $query", 'pg.'._LOG_AVERTISSEMENT);
$f = 'spip_pg_alter_' . strtolower($r[3]);
if (function_exists($f))
$f($r[2], $r[4], $serveur, $requeter);
else spip_log("$query non prevu", 'pg.'._LOG_ERREUR);
}
// Alter a plusieurs args. Faudrait optimiser.
if ($todo)
spip_pg_alter("TABLE $table " . join(',',$todo));
}
// http://doc.spip.org/@spip_pg_alter_change
function spip_pg_alter_change($table, $arg, $serveur='',$requeter=true)
{
if (!preg_match('/^`?(\w+)`?\s+`?(\w+)`?\s+(.*?)\s*(DEFAULT .*?)?(NOT\s+NULL)?\s*(DEFAULT .*?)?$/i',$arg, $r)) {
spip_log("alter change: $arg incompris", 'pg.'._LOG_ERREUR);
} else {
list(,$old, $new, $type, $default, $null, $def2) = $r;
$actions = array("ALTER $old TYPE " . mysql2pg_type($type));
if ($null)
$actions[]= "ALTER $old SET NOT NULL";
else
$actions[]= "ALTER $old DROP NOT NULL";
if ($d = ($default ? $default : $def2))
$actions[]= "ALTER $old SET $d";
else
$actions[]= "ALTER $old DROP DEFAULT";
spip_pg_query("ALTER TABLE $table " . join(', ', $actions));
if ($old != $new)
spip_pg_query("ALTER TABLE $table RENAME $old TO $new", $serveur);
}
}
// http://doc.spip.org/@spip_pg_alter_add
function spip_pg_alter_add($table, $arg, $serveur='',$requeter=true) {
if (!preg_match('/^(COLUMN|INDEX|KEY|PRIMARY\s+KEY|)\s*(.*)$/', $arg, $r)) {
spip_log("alter add $arg incompris", 'pg.'._LOG_ERREUR);
return NULL;
}
if (!$r[1] OR $r[1]=='COLUMN') {
preg_match('/`?(\w+)`?(.*)/',$r[2], $m);
if (preg_match('/^(.*)(BEFORE|AFTER|FIRST)(.*)$/is', $m[2], $n)) {
$m[2]=$n[1];
}
return spip_pg_query("ALTER TABLE $table ADD " . $m[1] . ' ' . mysql2pg_type($m[2]), $serveur, $requeter);
} elseif ($r[1][0] == 'P') {
// la primary peut etre sur plusieurs champs
$r[2] = trim(str_replace('`','',$r[2]));
$m = ($r[2][0]=='(') ? substr($r[2],1,-1) : $r[2];
return spip_pg_query("ALTER TABLE $table ADD CONSTRAINT $table" .'_pkey PRIMARY KEY (' . $m . ')', $serveur, $requeter);
} else {
preg_match('/([^\s,]*)\s*(.*)?/',$r[2], $m);
// peut etre "(colonne)" ou "nom_index (colonnes)"
// bug potentiel si qqn met "(colonne, colonne)"
//
// nom_index (colonnes)
if ($m[2]) {
$colonnes = substr($m[2],1,-1);
$nom_index = $m[1];
}
else {
// (colonne)
if ($m[1][0] == "(") {
$colonnes = substr($m[1],1,-1);
if (false!==strpos(",",$colonnes)) {
spip_log(_LOG_GRAVITE_ERREUR,"PG : Erreur, impossible de creer un index sur plusieurs colonnes"
." sans qu'il ait de nom ($table, ($colonnes))", 'pg');
} else {
$nom_index = $colonnes;
}
}
// nom_index
else {
$nom_index = $colonnes = $m[1];
}
}
return spip_pg_create_index($nom_index, $table, $colonnes, $serveur, $requeter);
}
}
// http://doc.spip.org/@spip_pg_alter_drop
function spip_pg_alter_drop($table, $arg, $serveur='',$requeter=true) {
if (!preg_match('/^(COLUMN|INDEX|KEY|PRIMARY\s+KEY|)\s*`?(\w*)`?/', $arg, $r))
spip_log("alter drop: $arg incompris", 'pg.'._LOG_ERREUR);
else {
if (!$r[1] OR $r[1]=='COLUMN')
return spip_pg_query("ALTER TABLE $table DROP " . $r[2], $serveur);
elseif ($r[1][0] == 'P')
return spip_pg_query("ALTER TABLE $table DROP CONSTRAINT $table" . '_pkey', $serveur);
else {
return spip_pg_query("DROP INDEX " . $table . '_' . $r[2], $serveur);
}
}
}
function spip_pg_alter_modify($table, $arg, $serveur='',$requeter=true) {
if (!preg_match('/^`?(\w+)`?\s+(.*)$/',$arg, $r)) {
spip_log("alter modify: $arg incompris", 'pg.'._LOG_ERREUR);
} else {
return spip_pg_alter_change($table, $r[1].' '.$arg, $serveur='',$requeter=true);
}
}
// attention (en pg) :
// - alter table A rename to X = changer le nom de la table
// - alter table A rename X to Y = changer le nom de la colonne X en Y
// pour l'instant, traiter simplement RENAME TO X
function spip_pg_alter_rename($table, $arg, $serveur='',$requeter=true) {
$rename="";
// si TO, mais pas au debut
if (!stripos($arg,'TO ')){
$rename=$arg;
}
elseif (preg_match('/^(TO)\s*`?(\w*)`?/', $arg, $r)) {
$rename=$r[2];
} else {
spip_log("alter rename: $arg incompris", 'pg.'._LOG_ERREUR);
}
return $rename?spip_pg_query("ALTER TABLE $table RENAME TO $rename"):false;
}
/**
* Fonction de creation d'un INDEX
*
* @param string $nom : nom de l'index
* @param string $table : table sql de l'index
* @param string/array $champs : liste de champs sur lesquels s'applique l'index
* @param string $serveur : nom de la connexion sql utilisee
* @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
*
* @return bool ou requete
*/
function spip_pg_create_index($nom, $table, $champs, $serveur='', $requeter=true) {
if (!($nom OR $table OR $champs)) {
spip_log("Champ manquant pour creer un index pg ($nom, $table, (".@join(',',$champs)."))",'pg.'._LOG_ERREUR);
return false;
}
$nom = str_replace("`","",$nom);
$champs = str_replace("`","",$champs);
// PG ne differentie pas noms des index en fonction des tables
// il faut donc creer des noms uniques d'index pour une base pg
$nom = $table.'_'.$nom;
// enlever d'eventuelles parentheses deja presentes sur champs
if (!is_array($champs)){
if ($champs[0]=="(") $champs = substr($champs,1,-1);
$champs = array($champs);
}
$query = "CREATE INDEX $nom ON $table (" . join(',',$champs) . ")";
if (!$requeter) return $query;
$res = spip_pg_query($query, $serveur, $requeter);
return $res;
}
// http://doc.spip.org/@spip_pg_explain
function spip_pg_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'];
if (preg_match('/\s(SET|VALUES|WHERE)\s/i', $query, $regs)) {
$suite = strstr($query, $regs[0]);
$query = substr($query, 0, -strlen($suite));
} else $suite ='';
$query = 'EXPLAIN ' . preg_replace('/([,\s])spip_/', '\1'.$prefixe.'_', $query) . $suite;
if (!$requeter) return $query;
$r = spip_pg_query_simple($link,$query);
return spip_pg_fetch($r, NULL, $serveur);
}
/**
* Selectionne une base de donnees
*
* @param string $nom
* Nom de la base a utiliser
* @param string $serveur
* Nom du connecteur
* @param bool $requeter
* Inutilise
*
* @return bool|string
* Nom de la base en cas de success.
* False en cas d'erreur.
**/
function spip_pg_selectdb($db, $serveur='',$requeter=true) {
// se connecter a la base indiquee
// avec les identifiants connus
$index = $serveur ? strtolower($serveur) : 0;
if ($link = spip_connect_db('', '', '', '', $db, 'pg', '', '')){
if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
return $db;
} else
return false;
}
// Qu'une seule base pour le moment
// http://doc.spip.org/@spip_pg_listdbs
function spip_pg_listdbs($serveur) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$link = $connexion['link'];
$dbs = array();
$res = spip_pg_query_simple($link, "select * From pg_database");
while ($row = pg_fetch_array($res, NULL, PGSQL_NUM))
$dbs[] = reset($row);
return $dbs;
}
// http://doc.spip.org/@spip_pg_select
function spip_pg_select($select, $from, $where='',
$groupby=array(), $orderby='', $limit='',
$having='', $serveur='',$requeter=true){
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
$limit = preg_match("/^\s*(([0-9]+),)?\s*([0-9]+)\s*$/", $limit,$limatch);
if ($limit) {
$offset = $limatch[2];
$count = $limatch[3];
}
$select = spip_pg_frommysql($select);
// si pas de tri explicitement demande, le GROUP BY ne
// contient que la clef primaire.
// lui ajouter alors le champ de tri par defaut
if (preg_match("/FIELD\(([a-z]+\.[a-z]+),/i", $orderby[0], $groupbyplus)) {
$groupby[] = $groupbyplus[1];
}
$orderby = spip_pg_orderby($orderby, $select);
if ($having) {
if (is_array($having))
$having = join("\n\tAND ", array_map('calculer_pg_where', $having));
}
$from = spip_pg_from($from, $prefixe);
$query = "SELECT ". $select
. (!$from ? '' : "\nFROM $from")
. (!$where ? '' : ("\nWHERE " . (!is_array($where) ? calculer_pg_where($where) : (join("\n\tAND ", array_map('calculer_pg_where', $where))))))
. spip_pg_groupby($groupby, $from, $select)
. (!$having ? '' : "\nHAVING $having")
. ($orderby ? ("\nORDER BY $orderby") :'')
. (!$limit ? '' : (" LIMIT $count" . (!$offset ? '' : " OFFSET $offset")));
// renvoyer la requete inerte si demandee
if ($requeter === false) return $query;
$r = spip_pg_trace_query($query, $serveur);
return $r ? $r : $query;;
}
// Le traitement des prefixes de table dans un Select se limite au FROM
// car le reste de la requete utilise les alias (AS) systematiquement
// http://doc.spip.org/@spip_pg_from
function spip_pg_from($from, $prefixe)
{
if (is_array($from)) $from = spip_pg_select_as($from);
return !$prefixe ? $from : preg_replace('/(\b)spip_/','\1'.$prefixe.'_', $from);
}
// http://doc.spip.org/@spip_pg_orderby
function spip_pg_orderby($order, $select)
{
$res = array();
$arg = (is_array($order) ? $order : preg_split('/\s*,\s*/',$order));
foreach($arg as $v) {
if (preg_match('/(case\s+.*?else\s+0\s+end)\s*AS\s+' . $v .'/', $select, $m)) {
$res[] = $m[1];
} else $res[]=$v;
}
return spip_pg_frommysql(join(',',$res));
}
// Conversion a l'arrach' des jointures MySQL en jointures PG
// A refaire pour tirer parti des possibilites de PG et de MySQL5
// et pour enlever les repetitions (sans incidence de perf, mais ca fait sale)
// http://doc.spip.org/@spip_pg_groupby
function spip_pg_groupby($groupby, $from, $select)
{
$join = strpos($from, ",");
// ismplifier avant de decouper
if (is_string($select))
// fct SQL sur colonne et constante apostrophee ==> la colonne
$select = preg_replace('/\w+\(\s*([^(),\']*),\s*\'[^\']*\'[^)]*\)/','\\1', $select);
if ($join OR $groupby) $join = is_array($select) ? $select : explode(", ", $select);
if ($join) {
// enlever les 0 as points, '', ...
foreach($join as $k=>$v){
$v = str_replace('DISTINCT ','',$v);
// fct SQL sur colonne et constante apostrophee ==> la colonne
$v = preg_replace('/\w+\(\s*([^(),\']*),\s*\'[^\']*\'[^)]*\)/','\\1', $v);
$v = preg_replace('/CAST\(\s*([^(),\' ]*\s+)as\s*\w+\)/','\\1', $v);
// resultat d'agregat ne sont pas a mettre dans le groupby
$v = preg_replace('/(SUM|COUNT|MAX|MIN|UPPER)\([^)]+\)(\s*AS\s+\w+)\s*,?/i','', $v);
// idem sans AS (fetch numerique)
$v = preg_replace('/(SUM|COUNT|MAX|MIN|UPPER)\([^)]+\)\s*,?/i','', $v);
// des AS simples : on garde le cote droit du AS
$v = preg_replace('/^.*\sAS\s+(\w+)\s*$/i','\\1', $v);
// ne reste plus que les vrais colonnes, ou des constantes a virer
if (preg_match(',^[\'"],',$v) OR is_numeric($v))
unset($join[$k]);
else
$join[$k] = trim($v);
}
$join = array_diff($join,array(''));
$join = implode(',',$join);
}
if (is_array($groupby)) $groupby = join(',',$groupby);
if ($join) $groupby = $groupby ? "$groupby, $join" : $join;
if (!$groupby) return '';
$groupby = spip_pg_frommysql($groupby);
// Ne pas mettre dans le Group-By des valeurs numeriques
// issue de prepare_recherche
$groupby = preg_replace('/^\s*\d+\s+AS\s+\w+\s*,?\s*/i','', $groupby);
$groupby = preg_replace('/,\s*\d+\s+AS\s+\w+\s*/i','', $groupby);
$groupby = preg_replace('/\s+AS\s+\w+\s*/i','', $groupby);
return "\nGROUP BY $groupby";
}
// Conversion des operateurs MySQL en PG
// IMPORTANT: "0+X" est vu comme conversion numerique du debut de X
// Les expressions de date ne sont pas gerees au-dela de 3 ()
// Le 'as' du 'CAST' est en minuscule pour echapper au dernier preg_replace
// de spip_pg_groupby.
// A ameliorer.
// http://doc.spip.org/@spip_pg_frommysql
function spip_pg_frommysql($arg)
{
if (is_array($arg)) $arg = join(", ", $arg);
$res = spip_pg_fromfield($arg);
$res = preg_replace('/\brand[(][)]/i','random()', $res);
$res = preg_replace('/\b0\.0[+]([a-zA-Z0-9_.]+)\s*/',
'CAST(substring(\1, \'^ *[0-9.]+\') as float)',
$res);
$res = preg_replace('/\b0[+]([a-zA-Z0-9_.]+)\s*/',
'CAST(substring(\1, \'^ *[0-9]+\') as int)',
$res);
$res = preg_replace('/\bconv[(]([^,]*)[^)]*[)]/i',
'CAST(substring(\1, \'^ *[0-9]+\') as int)',
$res);
$res = preg_replace('/UNIX_TIMESTAMP\s*[(]\s*[)]/',
' EXTRACT(epoch FROM NOW())', $res);
// la fonction md5(integer) n'est pas connu en pg
// il faut donc forcer les types en text (cas de md5(id_article))
$res = preg_replace('/md5\s*[(]([^)]*)[)]/i',
'MD5(CAST(\1 AS text))', $res);
$res = preg_replace('/UNIX_TIMESTAMP\s*[(]([^)]*)[)]/',
' EXTRACT(epoch FROM \1)', $res);
$res = preg_replace('/\bDAYOFMONTH\s*[(]([^()]*([(][^()]*[)][^()]*)*[^)]*)[)]/',
' EXTRACT(day FROM \1)',
$res);
$res = preg_replace('/\bMONTH\s*[(]([^()]*([(][^)]*[)][^()]*)*[^)]*)[)]/',
' EXTRACT(month FROM \1)',
$res);
$res = preg_replace('/\bYEAR\s*[(]([^()]*([(][^)]*[)][^()]*)*[^)]*)[)]/',
' EXTRACT(year FROM \1)',
$res);
$res = preg_replace('/TO_DAYS\s*[(]([^()]*([(][^)]*[)][()]*)*)[)]/',
' EXTRACT(day FROM \1 - \'0001-01-01\')',
$res);
$res = preg_replace("/(EXTRACT[(][^ ]* FROM *)\"([^\"]*)\"/", '\1\'\2\'', $res);
$res = preg_replace('/DATE_FORMAT\s*[(]([^,]*),\s*\'%Y%m%d\'[)]/', 'to_char(\1, \'YYYYMMDD\')', $res);
$res = preg_replace('/DATE_FORMAT\s*[(]([^,]*),\s*\'%Y%m\'[)]/', 'to_char(\1, \'YYYYMM\')', $res);
$res = preg_replace('/DATE_SUB\s*[(]([^,]*),/', '(\1 -', $res);
$res = preg_replace('/DATE_ADD\s*[(]([^,]*),/', '(\1 +', $res);
$res = preg_replace('/INTERVAL\s+(\d+\s+\w+)/', 'INTERVAL \'\1\'', $res);
$res = preg_replace('/([+<>-]=?)\s*(\'\d+-\d+-\d+\s+\d+:\d+(:\d+)\')/', '\1 timestamp \2', $res);
$res = preg_replace('/(\'\d+-\d+-\d+\s+\d+:\d+:\d+\')\s*([+<>-]=?)/', 'timestamp \1 \2', $res);
$res = preg_replace('/([+<>-]=?)\s*(\'\d+-\d+-\d+\')/', '\1 timestamp \2', $res);
$res = preg_replace('/(\'\d+-\d+-\d+\')\s*([+<>-]=?)/', 'timestamp \1 \2', $res);
$res = preg_replace('/(timestamp .\d+)-00-/','\1-01-', $res);
$res = preg_replace('/(timestamp .\d+-\d+)-00/','\1-01',$res);
# correct en theorie mais produit des debordements arithmetiques
# $res = preg_replace("/(EXTRACT[(][^ ]* FROM *)(timestamp *'[^']*' *[+-] *timestamp *'[^']*') *[)]/", '\2', $res);
$res = preg_replace("/(EXTRACT[(][^ ]* FROM *)('[^']*')/", '\1 timestamp \2', $res);
$res = preg_replace("/\sLIKE\s+/", ' ILIKE ', $res);
return str_replace('REGEXP', '~', $res);
}
// http://doc.spip.org/@spip_pg_fromfield
function spip_pg_fromfield($arg)
{
while(preg_match('/^(.*?)FIELD\s*\(([^,]*)((,[^,)]*)*)\)/', $arg, $m)) {
preg_match_all('/,([^,]*)/', $m[3], $r, PREG_PATTERN_ORDER);
$res = '';
$n=0;
$index = $m[2];
foreach($r[1] as $v) {
$n++;
$res .= "\nwhen $index=$v then $n";
}
$arg = $m[1] . "case $res else 0 end "
. substr($arg,strlen($m[0]));
}
return $arg;
}
// http://doc.spip.org/@calculer_pg_where
function calculer_pg_where($v)
{
if (!is_array($v))
return spip_pg_frommysql($v);
$op = str_replace('REGEXP', '~', array_shift($v));
if (!($n=count($v)))
return $op;
else {
$arg = calculer_pg_where(array_shift($v));
if ($n==1) {
return "$op($arg)";
} else {
$arg2 = calculer_pg_where(array_shift($v));
if ($n==2) {
return "($arg $op $arg2)";
} else return "($arg $op ($arg2) : $v[0])";
}
}
}
// http://doc.spip.org/@calculer_pg_expression
function calculer_pg_expression($expression, $v, $join = 'AND'){
if (empty($v))
return '';
$exp = "\n$expression ";
if (!is_array($v)) $v = array($v);
if (strtoupper($join) === 'AND')
return $exp . join("\n\t$join ", array_map('calculer_pg_where', $v));
else
return $exp . join($join, $v);
}
// http://doc.spip.org/@spip_pg_select_as
function spip_pg_select_as($args)
{
$argsas = "";
foreach($args as $k => $v) {
if (substr($k,-1)=='@') {
// c'est une jointure qui se refere au from precedent
// pas de virgule
$argsas .= ' ' . $v ;
}
else {
$as = '';
// spip_log("$k : $v", _LOG_DEBUG);
if (!is_numeric($k)) {
if (preg_match('/\.(.*)$/', $k, $r))
$v = $k;
elseif ($v != $k) {
$p = strpos($v, " ");
if ($p)
$v = substr($v,0,$p) . " AS $k" . substr($v,$p);
else $as = " AS $k";
}
}
// spip_log("subs $k : $v avec $as", _LOG_DEBUG);
// if (strpos($v, 'JOIN') === false) $argsas .= ', ';
$argsas .= ', '. $v . $as;
}
}
return substr($argsas,2);
}
// http://doc.spip.org/@spip_pg_fetch
function spip_pg_fetch($res, $t='', $serveur='',$requeter=true) {
if ($res) $res = pg_fetch_array($res, NULL, PGSQL_ASSOC);
return $res;
}
function spip_pg_seek($r, $row_number, $serveur='',$requeter=true) {
if ($r) return pg_result_seek($r,$row_number);
}
// http://doc.spip.org/@spip_pg_countsel
function spip_pg_countsel($from = array(), $where = array(), $groupby=array(),
$having = array(), $serveur='',$requeter=true)
{
$c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
$r = spip_pg_select("COUNT($c)", $from, $where,'', '', '', $having, $serveur, $requeter);
if (!$requeter) return $r;
if (!is_resource($r)) return 0;
list($c) = pg_fetch_array($r, NULL, PGSQL_NUM);
return $c;
}
// http://doc.spip.org/@spip_pg_count
function spip_pg_count($res, $serveur='',$requeter=true) {
return !$res ? 0 : pg_numrows($res);
}
// http://doc.spip.org/@spip_pg_free
function spip_pg_free($res, $serveur='',$requeter=true) {
// rien a faire en postgres
}
// http://doc.spip.org/@spip_pg_delete
function spip_pg_delete($table, $where='', $serveur='',$requeter=true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
$query = calculer_pg_expression('DELETE FROM', $table, ',')
. calculer_pg_expression('WHERE', $where, 'AND');
// renvoyer la requete inerte si demandee
if (!$requeter) return $query;
$res = spip_pg_trace_query($query, $serveur);
if ($res)
return pg_affected_rows($res);
else
return false;
}
// http://doc.spip.org/@spip_pg_insert
function spip_pg_insert($table, $champs, $valeurs, $desc=array(), $serveur='',$requeter=true) {
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if (!$desc) $desc = description_table($table, $serveur);
$seq = spip_pg_sequence($table,true);
// si pas de cle primaire dans l'insertion, renvoyer curval
if (!preg_match(",\b$seq\b,",$champs)){
$seq = spip_pg_sequence($table);
if ($prefixe)
$seq = preg_replace('/^spip/', $prefixe, $seq);
$seq = "currval('$seq')";
}
if ($prefixe) {
$table = preg_replace('/^spip/', $prefixe, $table);
}
$ret = !$seq ? '' : (" RETURNING $seq");
$ins = (strlen($champs)<3)
? " DEFAULT VALUES"
: "$champs VALUES $valeurs";
$q ="INSERT INTO $table $ins $ret";
if (!$requeter) return $q;
$connexion['last'] = $q;
$r = spip_pg_query_simple($link, $q);
# spip_log($q,'pg.'._LOG_DEBUG);
if ($r) {
if (!$ret) return 0;
if ($r2 = pg_fetch_array($r, NULL, PGSQL_NUM))
return $r2[0];
}
return false;
}
// http://doc.spip.org/@spip_pg_insertq
function spip_pg_insertq($table, $couples=array(), $desc=array(), $serveur='',$requeter=true) {
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
$fields = $desc['field'];
foreach ($couples as $champ => $val) {
$couples[$champ]= spip_pg_cite($val, $fields[$champ]);
}
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$couples = spip_pg_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
return spip_pg_insert($table, "(".join(',',array_keys($couples)).")", "(".join(',', $couples).")", $desc, $serveur, $requeter);
}
// http://doc.spip.org/@spip_pg_insertq_multi
function spip_pg_insertq_multi($table, $tab_couples=array(), $desc=array(), $serveur='',$requeter=true) {
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
$fields = isset($desc['field'])?$desc['field']:array();
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
// une premiere fois pour ajouter maj dans les cles
$c = isset($tab_couples[0]) ? $tab_couples[0] : array();
$les_cles = spip_pg_ajouter_champs_timestamp($table, $c, $desc, $serveur);
$cles = "(" . join(',',array_keys($les_cles)). ')';
$valeurs = array();
foreach ($tab_couples as $couples) {
foreach ($couples as $champ => $val){
$couples[$champ]= spip_pg_cite($val, $fields[$champ]);
}
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$couples = spip_pg_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
$valeurs[] = '(' .join(',', $couples) . ')';
}
$valeurs = implode(', ',$valeurs);
return spip_pg_insert($table, $cles, $valeurs, $desc, $serveur, $requeter);
}
// http://doc.spip.org/@spip_pg_update
function spip_pg_update($table, $couples, $where='', $desc='', $serveur='',$requeter=true) {
if (!$couples) return;
$connexion = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$couples = spip_pg_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
$set = array();
foreach ($couples as $champ => $val) {
$set[] = $champ . '=' . $val;
}
$query = calculer_pg_expression('UPDATE', $table, ',')
. calculer_pg_expression('SET', $set, ',')
. calculer_pg_expression('WHERE', $where, 'AND');
// renvoyer la requete inerte si demandee
if (!$requeter) return $query;
return spip_pg_trace_query($query, $serveur);
}
// idem, mais les valeurs sont des constantes a mettre entre apostrophes
// sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
// http://doc.spip.org/@spip_pg_updateq
function spip_pg_updateq($table, $couples, $where='', $desc=array(), $serveur='',$requeter=true) {
if (!$couples) return;
if (!$desc) $desc = description_table($table, $serveur);
$fields = $desc['field'];
foreach ($couples as $k => $val) {
$couples[$k] = spip_pg_cite($val, $fields[$k]);
}
return spip_pg_update($table, $couples, $where, $desc, $serveur, $requeter);
}
// http://doc.spip.org/@spip_pg_replace
function spip_pg_replace($table, $values, $desc, $serveur='',$requeter=true) {
if (!$values) {spip_log("replace vide $table",'pg.'._LOG_AVERTISSEMENT); return 0;}
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if (!$desc) $desc = description_table($table, $serveur);
if (!$desc) die("$table insertion sans description");
$prim = $desc['key']['PRIMARY KEY'];
$ids = preg_split('/,\s*/', $prim);
$noprims = $prims = array();
foreach($values as $k=>$v) {
$values[$k] = $v = spip_pg_cite($v, $desc['field'][$k]);
if (!in_array($k, $ids))
$noprims[$k]= "$k=$v";
else $prims[$k]= "$k=$v";
}
// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
$values = spip_pg_ajouter_champs_timestamp($table, $values, $desc, $serveur);
$where = join(' AND ', $prims);
if (!$where) {
return spip_pg_insert($table, "(".join(',',array_keys($values)).")", "(".join(',', $values).")", $desc, $serveur);
}
$couples = join(',', $noprims);
$seq = spip_pg_sequence($table);
if ($prefixe) {
$table = preg_replace('/^spip/', $prefixe, $table);
$seq = preg_replace('/^spip/', $prefixe, $seq);
}
$connexion['last'] = $q = "UPDATE $table SET $couples WHERE $where";
if ($couples) {
$couples = spip_pg_query_simple($link, $q);
# spip_log($q,'pg.'._LOG_DEBUG);
if (!$couples) return false;
$couples = pg_affected_rows($couples);
}
if (!$couples) {
$ret = !$seq ? '' :
(" RETURNING nextval('$seq') < $prim");
$connexion['last'] = $q = "INSERT INTO $table (" . join(',',array_keys($values)) . ') VALUES (' .join(',', $values) . ")$ret";
$couples = spip_pg_query_simple($link, $q);
if (!$couples) {
return false;
} elseif ($ret) {
$r = pg_fetch_array($couples, NULL, PGSQL_NUM);
if ($r[0]) {
$connexion['last'] = $q = "SELECT setval('$seq', $prim) from $table";
// Le code de SPIP met parfois la sequence a 0 (dans l'import)
// MySQL n'en dit rien, on fait pareil pour PG
$r = @pg_query($link, $q);
}
}
}
return $couples;
}
// http://doc.spip.org/@spip_pg_replace_multi
function spip_pg_replace_multi($table, $tab_couples, $desc=array(), $serveur='',$requeter=true) {
// boucler pour traiter chaque requete independemment
foreach ($tab_couples as $couples){
$retour = spip_pg_replace($table, $couples, $desc, $serveur,$requeter);
}
// renvoie le dernier id
return $retour;
}
// Donne la sequence eventuelle associee a une table
// Pas extensible pour le moment,
// http://doc.spip.org/@spip_pg_sequence
function spip_pg_sequence($table,$raw=false)
{
global $tables_principales;
include_spip('base/serial');
if (!isset($tables_principales[$table])) return false;
$desc = $tables_principales[$table];
$prim = @$desc['key']['PRIMARY KEY'];
if (!preg_match('/^\w+$/', $prim)
OR strpos($desc['field'][$prim], 'int') === false)
return '';
else { return $raw?$prim:$table . '_' . $prim . "_seq";}
}
// Explicite les conversions de Mysql d'une valeur $v de type $t
// Dans le cas d'un champ date, pas d'apostrophe, c'est une syntaxe ad hoc
// http://doc.spip.org/@spip_pg_cite
function spip_pg_cite($v, $t){
if(is_null($v)) return 'NULL'; // null php se traduit en NULL SQL
if (sql_test_date($t)) {
if ($v AND (strpos("0123456789", $v[0]) === false))
return spip_pg_frommysql($v);
else {
if (strncmp($v,'0000',4)==0)
$v = "0001" . substr($v,4);
if (strpos($v, "-00-00") === 4)
$v = substr($v,0,4)."-01-01".substr($v,10);
return "timestamp '$v'";
}
}
elseif (!sql_test_int($t))
return ("'" . pg_escape_string($v) . "'");
elseif (is_numeric($v) OR (strpos($v, 'CAST(') === 0))
return $v;
elseif ($v[0]== '0' AND $v[1]!=='x' AND ctype_xdigit(substr($v,1)))
return substr($v,1);
else {
spip_log("Warning: '$v' n'est pas de type $t", 'pg.'._LOG_AVERTISSEMENT);
return intval($v);
}
}
// http://doc.spip.org/@spip_pg_hex
function spip_pg_hex($v)
{
return "CAST(x'" . $v . "' as bigint)";
}
function spip_pg_quote($v, $type='')
{
if (!is_array($v))
return spip_pg_cite($v,$type);
// si c'est un tableau, le parcourir en propageant le type
foreach($v as $k=>$r)
$v[$k] = spip_pg_quote($r, $type);
return join(",", $v);
}
function spip_pg_date_proche($champ, $interval, $unite)
{
return '('
. $champ
. (($interval <= 0) ? '>' : '<')
. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
. '('
. sql_quote(date('Y-m-d H:i:s'))
. ', INTERVAL '
. (($interval > 0) ? $interval : (0-$interval))
. ' '
. $unite
. '))';
}
// http://doc.spip.org/@spip_pg_in
function spip_pg_in($val, $valeurs, $not='', $serveur) {
//
// IN (...) souvent limite a 255 elements, d'ou cette fonction assistante
//
// s'il n'y a pas de valeur, eviter de produire un IN vide: PG rale.
if (!$valeurs) return $not ? '0=0' : '0=1';
if (strpos($valeurs, "CAST(x'") !== false)
return "($val=" . join("OR $val=", explode(',',$valeurs)).')';
$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)";
}
// http://doc.spip.org/@spip_pg_error
function spip_pg_error($query='', $serveur, $requeter=true) {
$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
$s = $link ? pg_last_error($link) : pg_last_error();
if ($s) {
$s = str_replace('ERROR', 'errcode: 1000 ', $s);
spip_log("$s - $query", 'pg.'._LOG_ERREUR);
}
return $s;
}
// http://doc.spip.org/@spip_pg_errno
function spip_pg_errno($serveur='') {
// il faudrait avoir la derniere ressource retournee et utiliser
// http://fr2.php.net/manual/fr/function.pg-result-error.php
return 0;
}
// http://doc.spip.org/@spip_pg_drop_table
function spip_pg_drop_table($table, $exist='', $serveur='',$requeter=true)
{
if ($exist) $exist =" IF EXISTS";
if (spip_pg_query("DROP TABLE$exist $table", $serveur, $requeter))
return true;
else return false;
}
// supprime une vue
// http://doc.spip.org/@spip_pg_drop_view
function spip_pg_drop_view($view, $exist='', $serveur='',$requeter=true) {
if ($exist) $exist =" IF EXISTS";
return spip_pg_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_pg_showbase($match, $serveur='',$requeter=true)
{
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$link = $connexion['link'];
$connexion['last'] = $q = "SELECT tablename FROM pg_tables WHERE tablename ILIKE "._q($match);
return spip_pg_query_simple($link, $q);
}
// http://doc.spip.org/@spip_pg_showtable
function spip_pg_showtable($nom_table, $serveur='',$requeter=true)
{
$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$link = $connexion['link'];
$connexion['last'] = $q = "SELECT column_name, column_default, data_type FROM information_schema.columns WHERE table_name ILIKE " . _q($nom_table);
$res = spip_pg_query_simple($link, $q);
if (!$res) return false;
// etrangement, $res peut ne rien contenir, mais arriver ici...
// il faut en tenir compte dans le return
$fields = array();
while($field = pg_fetch_array($res, NULL, PGSQL_NUM)) {
$fields[$field[0]] = $field[2] . (!$field[1] ? '' : (" DEFAULT " . $field[1]));
}
$connexion['last'] = $q = "SELECT indexdef FROM pg_indexes WHERE tablename ILIKE " . _q($nom_table);
$res = spip_pg_query_simple($link, $q);
$keys = array();
while($index = pg_fetch_array($res, NULL, PGSQL_NUM)) {
if (preg_match('/CREATE\s+(UNIQUE\s+)?INDEX\s([^\s]+).*\((.*)\)$/', $index[0],$r)) {
$nom = str_replace($nom_table.'_','',$r[2]);
$keys[($r[1] ? "PRIMARY KEY" : ("KEY " . $nom))] = $r[3];
}
}
return count($fields) ? array('field' => $fields, 'key' => $keys) : false;
}
// Fonction de creation d'une table SQL nommee $nom
// a partir de 2 tableaux PHP :
// champs: champ => type
// cles: type-de-cle => champ(s)
// si $autoinc, c'est une auto-increment (i.e. serial) sur la Primary Key
// Le nom des index est prefixe par celui de la table pour eviter les conflits
// http://doc.spip.org/@spip_pg_create
function spip_pg_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $serveur='',$requeter=true) {
$connexion = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
$prefixe = $connexion['prefixe'];
$link = $connexion['link'];
$db = $connexion['db'];
if ($prefixe) $nom = preg_replace('/^spip/', $prefixe, $nom);
$query = $prim = $prim_name = $v = $s = $p='';
$keys = array();
// 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;
foreach($cles as $k => $v) {
if (strpos($k, "KEY ") === 0) {
$n = str_replace('`','',$k);
$v = str_replace('`','"',$v);
$i = $nom . preg_replace("/KEY +/", '_',$n);
if ($k != $n) $i = "\"$i\"";
$keys[] = "CREATE INDEX $i ON $nom ($v);";
}
elseif (strpos($k, "UNIQUE ") === 0) {
$k = preg_replace("/^UNIQUE +/", '',$k);
$prim .= "$s\n\t\tCONSTRAINT " . str_replace('`','"',$k) ." UNIQUE ($v)";
}
else {
$prim .= "$s\n\t\t" . str_replace('`','"',$k) ." ($v)";
}
if ($k == "PRIMARY KEY")
$prim_name = $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) {
$k = str_replace('`','"',$k);
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,i',$defs[1]) ){
$v = $defs[1] . $character_set . ' ' . substr($v,strlen($defs[1]));
}
}
$query .= "$s\n\t\t$k "
. (($autoinc && ($prim_name == $k) && preg_match(',\b(big|small|medium|tiny)?int\b,i', $v))
? " bigserial"
: mysql2pg_type($v)
);
$s = ",";
}
$temporary = $temporary ? 'TEMPORARY':'';
// En l'absence de "if not exists" en PG, on neutralise les erreurs
$q = "CREATE $temporary TABLE $nom ($query" . ($prim ? ",$prim" : '') . ")".
($character_set?" DEFAULT $character_set":"")
."\n";
if (!$requeter) return $q;
$connexion['last'] = $q;
$r = @pg_query($link, $q);
if (!$r)
spip_log("Impossible de creer cette table: $q",'pg.'._LOG_ERREUR);
else {
foreach($keys as $index) {pg_query($link, $index);}
}
return $r;
}
function spip_pg_create_base($nom, $serveur='',$requeter=true) {
return spip_pg_query("CREATE DATABASE $nom", $serveur, $requeter);
}
// Fonction de creation d'une vue SQL nommee $nom
// http://doc.spip.org/@spip_pg_create_view
function spip_pg_create_view($nom, $query_select, $serveur='',$requeter=true) {
if (!$query_select) return false;
// vue deja presente
if (sql_showtable($nom, false, $serveur)) {
if ($requeter) spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)",'pg.'._LOG_ERREUR);
return false;
}
$query = "CREATE VIEW $nom AS ". $query_select;
return spip_pg_query($query, $serveur, $requeter);
}
// http://doc.spip.org/@spip_pg_set_connect_charset
function spip_pg_set_connect_charset($charset, $serveur='',$requeter=true){
spip_log("changement de charset sql a ecrire en PG",'pg.'._LOG_ERREUR);
}
/**
* Optimise une table SQL
*
* @param $table nom de la table a optimiser
* @param $serveur nom de la connexion
* @param $requeter effectuer la requete ? sinon retourner son code
* @return bool|string true / false / requete
**/
// http://doc.spip.org/@spip_sqlite_optimize
function spip_pg_optimize($table, $serveur='',$requeter=true){
return spip_pg_query("VACUUM ". $table, $serveur, $requeter);
}
// Selectionner la sous-chaine dans $objet
// correspondant a $lang. Cf balise Multi de Spip
// http://doc.spip.org/@spip_pg_multi
function spip_pg_multi ($objet, $lang) {
$r = "regexp_replace("
. $objet
. ",'<multi>.*[[]"
. $lang
. "[]]([^[]*).*</multi>', E'\\\\1') AS multi";
return $r;
}
// Palanquee d'idiosyncrasies MySQL dans les creations de table
// A completer par les autres, mais essayer de reduire en amont.
// http://doc.spip.org/@mysql2pg_type
function mysql2pg_type($v){
$remplace = array(
'/auto_increment/i' => '', // non reconnu
'/bigint/i' => 'bigint',
'/mediumint/i' => 'mediumint',
'/smallint/i'=> 'smallint',
"/tinyint/i" => 'int',
'/int\s*[(]\s*\d+\s*[)]/i' => 'int',
"/longtext/i" => 'text',
"/mediumtext/i" => 'text',
"/tinytext/i" => 'text',
"/longblob/i" => 'text',
"/0000-00-00/" =>'0001-01-01',
"/datetime/i" => 'timestamp',
"/unsigned/i" => '',
"/double/i" => 'double precision',
'/VARCHAR\((\d+)\)\s+BINARY/i' => 'varchar(\1)',
"/ENUM *[(][^)]*[)]/i" => "varchar(255)",
'/(timestamp .* )ON .*$/is' => '\\1',
);
return preg_replace(array_keys($remplace),array_values($remplace),$v);
}
// Renvoie false si on n'a pas les fonctions pg (pour l'install)
// http://doc.spip.org/@spip_versions_pg
function spip_versions_pg(){
charger_php_extension('pgsql');
return function_exists('pg_connect');
}
?>