Your IP : 216.73.216.20


Current Path : /home/e/t/i/etiennec/www/f1f54/
Upload File :
Current File : /home/e/t/i/etiennec/www/f1f54/req.tar

sqlite2.php000066600000002611152502777540006662 0ustar00<?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.php000066600000026721152502777540010672 0ustar00<?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.php000066600000107550152502777540006454 0ustar00<?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.php000066600000217440152502777540010654 0ustar00<?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.php000066600000002630152502777540006664 0ustar00<?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.php000066600000130022152502777540005703 0ustar00<?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');	
}

?>