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://code.spip.net/@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(); $e = ''; } else { $t = 0; } $connexion['last'] = $query; $r = spip_pg_query_simple($link, $query); // Log de l'erreur eventuelle if ($e = spip_pg_errno($serveur)) { $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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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); } /** * Sélectionne une base de données * * @param string $db * Nom de la base à utiliser * @param string $serveur * Nom du connecteur * @param bool $requeter * Inutilisé * * @return bool|string * - Nom de la base en cas de succès. * - 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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@spip_pg_count function spip_pg_count($res, $serveur = '', $requeter = true) { return !$res ? 0 : pg_numrows($res); } // http://code.spip.net/@spip_pg_free function spip_pg_free($res, $serveur = '', $requeter = true) { // rien a faire en postgres } // http://code.spip.net/@spip_pg_delete function spip_pg_delete($table, $where = '', $serveur = '', $requeter = true) { $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]; $table = prefixer_table_spip($table, $connexion['prefixe']); $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://code.spip.net/@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']; 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); $seq = prefixer_table_spip($seq, $prefixe); $seq = "currval('$seq')"; } $table = prefixer_table_spip($table, $prefixe); $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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@spip_pg_update function spip_pg_update($table, $couples, $where = '', $desc = '', $serveur = '', $requeter = true) { if (!$couples) { return; } $connexion = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]; $table = prefixer_table_spip($table, $connexion['prefixe']); // 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://code.spip.net/@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://code.spip.net/@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']; 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); $table = prefixer_table_spip($table, $prefixe); $seq = prefixer_table_spip($seq, $prefixe); $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://code.spip.net/@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://code.spip.net/@spip_pg_sequence function spip_pg_sequence($table, $raw = false) { include_spip('base/serial'); if (!isset($GLOBALS['tables_principales'][$table])) { return false; } $desc = $GLOBALS['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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@spip_pg_create function spip_pg_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $serveur = '', $requeter = true) { $connexion = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]; $link = $connexion['link']; $nom = prefixer_table_spip($nom, $connexion['prefixe']); $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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@spip_pg_multi function spip_pg_multi($objet, $lang) { $r = "regexp_replace(" . $objet . ",'.*[[]" . $lang . "[]]([^[]*).*', 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://code.spip.net/@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://code.spip.net/@spip_versions_pg function spip_versions_pg() { charger_php_extension('pgsql'); return function_exists('pg_connect'); }