Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
Class.DbObj.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Anakeen
4  * @package FDL
5 */
6 /**
7  * This class is a generic DB Class that can be used to create objects
8  * based on the description of a DB Table.
9  *
10  * @author Anakeen
11  * @version $Id: Class.DbObj.php,v 1.58 2008/12/29 17:05:38 eric Exp $
12  * @package FDL
13  * @subpackage CORE
14  */
15 /**
16  */
17 
18 include_once ('Class.Log.php');
19 include_once ('Lib.Common.php');
20 
21 $CLASS_DBOBJ_PHP = '$Id: Class.DbObj.php,v 1.58 2008/12/29 17:05:38 eric Exp $';
22 /**
23  * This class is a generic DB Class that can be used to create objects
24  * based on the description of a DB Table. More Complex Objects will
25  * inherit from this basic Class.
26  *
27  */
28 class DbObj
29 {
30  /**
31  * the database connection resource
32  * @var resource
33  */
34  var $dbid = - 1;
35  /**
36  * coordinates to access to database
37  * @var string
38  */
39  var $dbaccess = '';
40  /**
41  * array of SQL fields use for the object
42  * @var array
43  */
44  var $fields = array(
45  '*'
46  );
47  /**
48  * name of the SQL table
49  * @var string
50  */
51  var $dbtable = '';
52 
53  public $id_fields;
54 
55  var $criterias = array();
56  /**
57  * array of other SQL fields, not in attribute of object
58  * @var array
59  */
60  var $sup_fields = array();
61  var $sup_where = array();
62  var $sup_tables = array();
63  var $fulltextfields = array();
64  /**
65  * sql field to order
66  * @var string
67  */
68  var $order_by = "";
69  /**
70  * indicates if fields has been affected
71  * @var string
72  * @see Affect()
73  */
74  var $isset = false; // indicate if fields has been affected (call affect methods)
75  static $savepoint = array();
76  static $lockpoint = array();
77  static private $masterLock = false;
78  static $sqlStrict = null;
79  /**
80  * @var string error message
81  */
82  public $msg_err = '';
83  /**
84  * @var int
85  */
86  public $err_code = '';
87  /**
88  * @var resource
89  */
90  public $res = '';
91  /**
92  * @var bool
93  */
94  public $debug = false;
95  public $sqlcreate;
96  public $sqlinit;
97  /**
98  * @var Log DbObj Log Object
99  */
100  public $log;
101  //----------------------------------------------------------------------------
102 
103  /**
104  * Database Object constructor
105  *
106  *
107  * @param string $dbaccess database specification
108  * @param int|array $id identifier of the object
109  * @param array $res array of result issue to QueryDb {@link QueryDb::Query()}
110  * @param resource $dbid the database connection resource
111  * @return bool false if error occured
112  */
113  function __construct($dbaccess = '', $id = '', $res = '', $dbid = 0)
114  {
115  if (!$dbaccess) $dbaccess = getDbAccess();
116  $this->dbaccess = $dbaccess;
117  $this->init_dbid();
118  //global ${$this->oname};
119  $this->log = new Log("", "DbObj", $this->dbtable);
120 
121  if ($this->dbid == 0) {
122  $this->dbid = - 1;
123  }
124 
125  $this->selectstring = "";
126  // SELECTED FIELDS
127  reset($this->fields);
128  foreach ($this->fields as $k => $v) {
129  $this->selectstring = $this->selectstring . $this->dbtable . "." . $v . ",";
130  $this->$v = "";
131  }
132 
133  reset($this->sup_fields);
134  foreach ($this->sup_fields as $k => $v) {
135  $this->selectstring = $this->selectstring . "" . $v . ",";
136  $this->$v = "";
137  }
138  $this->selectstring = substr($this->selectstring, 0, strlen($this->selectstring) - 1);
139  if (self::$sqlStrict === null) {
140  self::$sqlStrict = (getParam('CORE_SQLSTRICT') != 'no');
141  }
142  // select with the id
143  if (($id != '') || (is_array($id)) || (!isset($this->id_fields[0]))) {
144  $ret = $this->Select($id);
145 
146  return ($ret);
147  }
148  // affect with a query result
149  if (is_array($res)) {
150  $this->Affect($res);
151  }
152 
153  return TRUE;
154  }
155  /**
156  * Select object from its fields
157  * if fields has more then one variable, need to use an array
158  * @param int|array $id
159  * @return bool|string
160  */
161  function Select($id)
162  {
163  if (!$id) return false;
164  if ($this->dbid == - 1) return FALSE;
165 
166  $msg = $this->PreSelect($id);
167  if ($msg != '') return $msg;
168 
169  if ($this->dbtable == '') {
170  return ("error : No Tables");
171  }
172  $fromstr = "{$this->dbtable}";
173  if (is_array($this->sup_tables)) {
174  reset($this->sup_tables);
175  foreach ($this->sup_tables as $k => $v) {
176  $fromstr.= "," . $v;
177  }
178  }
179  $sql = "select {$this->selectstring} from {$fromstr} ";
180 
181  $count = 0;
182  if (is_array($id)) {
183  $count = 0;
184  $wherestr = " where ";
185  reset($this->id_fields);
186  foreach ($this->id_fields as $k => $v) {
187  if ($count > 0) {
188  $wherestr = $wherestr . " AND ";
189  }
190  $wherestr = $wherestr . "( " . $this->dbtable . "." . $v . "=E'" . pg_escape_string($id[$k]) . "' )";
191  $count = $count + 1;
192  //$this->$v = $id[$k];
193 
194  }
195  } else {
196  if (isset($this->id_fields[0])) {
197  $k = $this->id_fields[0];
198  //$this->$k = $id;
199  $wherestr = "where " . $this->dbtable . "." . $this->id_fields[0] . "=E'" . pg_escape_string($id) . "'";
200  } else {
201  $wherestr = "";
202  }
203  }
204  if (is_array($this->sup_where)) {
205  reset($this->sup_where);
206  foreach ($this->sup_where as $k => $v) {
207  $wherestr = $wherestr . " AND ";
208  $wherestr = $wherestr . "( " . $v . " )";
209  $count = $count + 1;
210  }
211  }
212 
213  $sql = $sql . " " . $wherestr;
214 
215  $resultat = $this->exec_query($sql);
216 
217  if ($this->numrows() > 0) {
218  $res = $this->fetch_array(0);
219  $this->Affect($res);
220  } else {
221  return FALSE;
222  }
223  $msg = $this->PostSelect($id);
224  if ($msg != '') return $msg;
225  return TRUE;
226  }
227  /**
228  * get all values in indexed array
229  * @return array
230  */
231  function getValues()
232  {
233  $r = array();
234  foreach ($this->fields as $k => $v) {
235  $r[$v] = $this->$v;
236  }
237  return $r;
238  }
239  /**
240  * affect object specific column values from this item
241  * use only when object is already affected
242  * @param array $fields sql field to affect
243  * @return bool true if OK false else
244  */
245  function affectColumn($fields, $reset = true)
246  {
247  if ($this->dbid == - 1) return FALSE;
248 
249  if (!$this->isAffected()) return false;
250  if (count($fields) == 0) return true;
251  if ($this->dbtable == '') {
252  return ("error : No Tables");
253  }
254  $fromstr = $this->dbtable;
255  $w = array();
256  foreach ($this->id_fields as $id) {
257  $w[] = "($id = E'" . pg_escape_string($this->$id) . "') ";
258  }
259  $sqlwhere = implode("and", $w);
260  $sqlselect = implode(",", $fields);
261 
262  $sql = "select $sqlselect from $fromstr where $sqlwhere";
263 
264  $resultat = $this->exec_query($sql);
265 
266  if ($this->numrows() > 0) {
267  $res = $this->fetch_array(0);
268  $this->affect($res, false, $reset);
269  } else {
270  return FALSE;
271  }
272  return TRUE;
273  }
274  /**
275  * affect object with a set of values
276  * @param array $array indexed array of values , index if the column attribute
277  */
278  function affect($array, $more = false, $reset = true)
279  {
280  foreach ($array as $k => $v) {
281  if (!is_integer($k)) {
282  $this->$k = $v;
283  }
284  }
285  $this->Complete();
286  $this->isset = true;
287  }
288  /**
289  * verify that the object exists in database
290  * test if object has fields id set
291  * if true values of the object has been set
292  * @æpi test if object if affected
293  * @see affect
294  * @return bool
295  */
296  function isAffected()
297  {
298  return $this->isset;
299  }
300  /**
301  * @see affect
302  */
303  function Complete()
304  {
305  // This function should be replaced by the Child Class
306 
307  }
308  /**
309  * Method use before Add method
310  * This method should be replaced by the Child Class
311  * if return error message, modify is aborded
312  * @return string error message, if no error empty string
313  * @see Add()
314  */
315  public function preInsert()
316  {
317  // This function should be replaced by the Child Class
318  return '';
319  }
320  /**
321  * Method use after Add method
322  * This method should be replaced by the Child Class
323  *
324  * @return string error message, if no error empty string, if message
325  * error not empty the Add method is not completed
326  * @see Add()
327  */
328  public function postInsert()
329  {
330  // This function should be replaced by the Child Class
331  return '';
332  }
333  /**
334  * Method use before Modify method
335  * This method should be replaced by the Child Class
336  * if return error message, modify is aborded
337  *
338  * @return string error message, if no error empty string
339  * @see Modify()
340  */
341  public function preUpdate()
342  {
343  // This function should be replaced by the Child Class
344  return '';
345  }
346  /**
347  * Method use after Modify method
348  * This method should be replaced by the Child Class
349  *
350  * @return string error message, if no error empty string, if message
351  * error not empty the Modify method is not completed
352  * @see Modify()
353  */
354  public function postUpdate()
355  {
356  return '';
357  // This function should be replaced by the Child Class
358 
359  }
360  /**
361  * if return error message, deletion is aborded
362  * @see delete
363  * @return string
364  */
365  public function preDelete()
366  {
367  return '';
368  // This function should be replaced by the Child Class
369 
370  }
371  /**
372  * Method use after delete method
373  * @see delete
374  * @return string
375  */
376  public function postDelete()
377  {
378  return '';
379  // This function should be replaced by the Child Class
380 
381  }
382  /**
383  * Method use before select method
384  * @param mixed $id the id use by select
385  * @see select
386  * @return string
387  */
388  public function preSelect($id)
389  {
390  // This function should be replaced by the Child Class
391  return '';
392  }
393  /**
394  * Method use after select method
395  * @param mixed $id the id use by select
396  * @see select
397  * @return string
398  */
399  public function postSelect($id)
400  {
401  return '';
402  // This function should be replaced by the Child Class
403 
404  }
405  /**
406  * Add the object to the database
407  * @param bool $nopost PostInsert method not apply if true
408  * @param bool $nopre PreInsert method not apply if true
409  * @return string error message, if no error empty string
410  * @see PreInsert()
411  * @see PostInsert()
412  */
413  function Add($nopost = false, $nopre = false)
414  {
415  if ($this->dbid == - 1) return FALSE;
416  $msg = '';
417  if (!$nopre) $msg = $this->PreInsert();
418  if ($msg != '') return $msg;
419 
420  $sfields = implode(",", $this->fields);
421  $sql = "insert into " . $this->dbtable . "($sfields) values (";
422 
423  $valstring = "";
424  reset($this->fields);
425  foreach ($this->fields as $k => $v) {
426  $valstring = $valstring . $this->lw(isset($this->$v) ? $this->$v : '') . ",";
427  }
428  $valstring = substr($valstring, 0, strlen($valstring) - 1);
429  $sql = $sql . $valstring . ")";
430  // requery execution
431  $msg_err = $this->exec_query($sql);
432 
433  if ($msg_err != '') {
434  return $msg_err;
435  }
436  $this->isset = true;
437  if (!$nopost) $msg = $this->PostInsert();
438  return $msg;
439  }
440  /**
441  * Save the object to the database
442  * @param bool $nopost PostUpdate() and method not apply if true
443  * @param string $sfields only this column will ne updated if empty all fields
444  * @param bool $nopre PreUpdate() method not apply if true
445  * @return string error message, if no error empty string
446  * @see PreUpdate()
447  * @see PostUpdate()
448  */
449  public function modify($nopost = false, $sfields = "", $nopre = false)
450  {
451  $msg = '';
452  if ($this->dbid == - 1) return FALSE;
453  if (!$nopre) $msg = $this->PreUpdate();
454  if ($msg != '') return $msg;
455  $sql = "update " . $this->dbtable . " set ";
456 
457  $nb_keys = 0;
458 
459  if (!is_array($sfields)) $fields = $this->fields;
460  else {
461  $fields = $sfields;
462  foreach ($this->id_fields as $k => $v) $fields[] = $v;
463  }
464 
465  $wstr = "";
466  foreach ($this->id_fields as $k => $v) {
467  $notset[$v] = "Y";
468  $nb_keys++;
469  $val = pg_escape_string($this->$v);
470  $wstr = $wstr . " " . $v . "=E'" . $val . "' AND";
471  }
472 
473  $setstr = "";
474  foreach ($fields as $k => $v) {
475  if (!isset($notset[$v])) {
476  $setstr = $setstr . " " . $v . "=" . $this->lw(isset($this->$v) ? $this->$v : '') . ",";
477  }
478  }
479  $setstr = substr($setstr, 0, strlen($setstr) - 1);
480  $wstr = substr($wstr, 0, strlen($wstr) - 3);
481  $sql.= $setstr;
482  if ($nb_keys > 0) {
483  $sql.= " where " . $wstr . ";";
484  }
485 
486  $msg_err = $this->exec_query($sql);
487  // sortie
488  if ($msg_err != '') {
489  return $msg_err;
490  }
491 
492  if (!$nopost) $msg = $this->PostUpdate();
493 
494  return $msg;
495  }
496  /**
497  * Delete the object on the database
498  * @param bool $nopost PostUpdate() and method not apply if true
499  * @return string error message, if no error empty string
500  */
501  public function delete($nopost = false)
502  {
503  $msg = $this->PreDelete();
504  if ($msg != '') return $msg;
505  $wherestr = "";
506  $count = 0;
507 
508  reset($this->id_fields);
509  foreach ($this->id_fields as $k => $v) {
510  if ($count > 0) {
511  $wherestr = $wherestr . " AND ";
512  }
513  $wherestr = $wherestr . "( " . $v . "=E'" . pg_escape_string($this->$v) . "' )";
514  $count++;
515  }
516  // suppression de l'enregistrement
517  $sql = "delete from " . $this->dbtable . " where " . $wherestr . ";";
518 
519  $msg_err = $this->exec_query($sql);
520 
521  if ($msg_err != '') {
522  return $msg_err;
523  }
524 
525  if (!$nopost) $msg = $this->PostDelete();
526  return $msg;
527  }
528  /**
529  * Add several objects to the database
530  * no post neither preInsert are called
531  * @param bool $nopost PostInsert method not apply if true
532  * @return string error message, if no error empty string
533  * @see PreInsert()
534  * @see PostInsert()
535  */
536  function Adds(&$tcopy, $nopost = false)
537  {
538  if ($this->dbid == - 1) return FALSE;
539  if (!is_array($tcopy)) return FALSE;
540  $msg = '';
541  $sfields = implode(",", $this->fields);
542  $sql = "copy " . $this->dbtable . "($sfields) from STDIN;\n";
543 
544  $trow = array();
545  foreach ($tcopy as $kc => $vc) {
546  $trow[$kc] = "";
547  foreach ($this->fields as $k => $v) {
548  $trow[$kc].= "" . ((isset($vc[$v])) ? $vc[$v] : ((($this->$v) != '') ? $this->$v : '\N')) . "\t";
549  //$trow[$kc][$k] .= ((isset($vc[$v]))?$vc[$v]:$this->$v);
550 
551  }
552  $trow[$kc] = substr($trow[$kc], 0, -1);
553  }
554  // query execution
555  $berr = pg_copy_from($this->dbid, $this->dbtable, $trow, "\t");
556 
557  if (!$berr) return sprintf(_("DbObj::Adds error in multiple insertion"));
558 
559  if (!$nopost) $msg = $this->PostInsert();
560  return $msg;
561  }
562  function lw($prop)
563  {
564  $result = (($prop == '') && ($prop !== 0)) ? "null" : "E'" . pg_escape_string($prop) . "'";
565  return $result;
566  }
567  function CloseConnect()
568  {
569  pg_close("$this->dbid");
570  return TRUE;
571  }
572 
573  function Create($nopost = false)
574  {
575  $msg = "";
576  if (isset($this->sqlcreate)) {
577  // step by step
578  if (is_array($this->sqlcreate)) {
579  foreach ($this->sqlcreate as $k => $sqlquery) {
580  $msg.= $this->exec_query($sqlquery, 1);
581  }
582  } else {
583  $sqlcmds = explode(";", $this->sqlcreate);
584  foreach ($sqlcmds as $k => $sqlquery) {
585  $msg.= $this->exec_query($sqlquery, 1);
586  }
587  }
588  $this->log->debug("DbObj::Create : " . print_r($this->sqlcreate, true));
589  }
590  if (isset($this->sqlinit)) {
591  $msg = $this->exec_query($this->sqlinit, 1);
592  $this->log->debug("Init : {$this->sqlinit}");
593  }
594  if ($msg != '') {
595  $this->log->info("DbObj::Create $msg");
596  return $msg;
597  }
598  if (!$nopost) $this->PostInit();
599  return ($msg);
600  }
601 
602  public function postInit()
603  {
604  }
605 
606  function init_dbid()
607  {
608 
609  if ($this->dbaccess == "") {
610  // don't test if file exist or must be searched in include_path
611  $this->dbaccess = getDbAccess();
612  }
613  $this->dbid = getDbid($this->dbaccess);
614  if ($this->dbid == 0) error_log(__METHOD__ . "null dbid");
615  return $this->dbid;
616  }
617 
618  protected function tryCreate()
619  {
620 
621  $this->err_code = pg_result_error_field($this->res, PGSQL_DIAG_SQLSTATE);
622 
623  $action_needed = "";
624 
625  if ($this->err_code != "") {
626  // http://www.postgresql.org/docs/8.3/interactive/errcodes-appendix.html
627  switch ($this->err_code) {
628  case "42P01":
629  // UNDEFINED TABLE
630  $action_needed = "create";
631  break;
632 
633  case "42703":
634  // UNDEFINED COLUMN
635  $action_needed = "update";
636  break;
637 
638  case "42P07":
639  // DUPLICATE TABLE
640  $action_needed = "none";
641  break;
642 
643  default:
644  break;
645  }
646  // error_log(__METHOD__ . sprintf('[%s]%s {%s} - %s', $this->msg_err, $this->err_code, $action_needed, $this->dbtable));
647  //print_r2(getDebugStack());print $sql;
648  //trigger_error('<pre>'.$this->msg_err."\n".$sql.'</pre>');
649 
650  }
651 
652  $originError = $this->msg_err;
653  switch ($action_needed) {
654  case "create":
655  $st = $this->Create();
656  if ($st == "") {
657  return true;
658  } else {
659 
660  $err = ErrorCode::getError('DB0003', $this->dbtable, $st);
661  $this->msg_err = $originError . "\n" . $err;
662  }
663  break;
664 
665  case "update":
666  return false;
667  // no more auto update
668  /*
669  $st = $this->Update();
670  if ($st == "") {
671  return true;
672  } else {
673 
674  $err = ErrorCode::getError('DB0004', $this->dbtable, $st);
675  $this->msg_err = $originError . "\n" . $err;
676  }
677  */
678  break;
679 
680  case "none":
681  $this->msg_err = "";
682  return true;
683  break;
684 
685  default:
686  break;
687  }
688  return false;
689  }
690  /**
691  * Send a request to database
692  * @param string $sql the query
693  * @param int $lvl level set to 0 (internal purpose only)
694  * @param bool $prepare set to true to use pg_prepare, restrict to use single query
695  * @throw Dcp\Db\Exception if query fail
696  * @return string error message if not strict mode
697  */
698  function exec_query($sql, $lvl = 0, $prepare = false)
699  {
700  global $SQLDELAY, $SQLDEBUG;
701 
702  if ($sql == "") return '';
703  $sqlt1 = '';
704  if ($SQLDEBUG) $sqlt1 = microtime(); // to test delay of request
705  $this->init_dbid();
706  $this->log->debug("exec_query : $sql");
707  $this->msg_err = $this->err_code = '';
708  if ($prepare) {
709  if (pg_send_prepare($this->dbid, '', $sql) === false) {
710  $this->msg_err = ErrorCode::getError('DB0006', pg_last_error($this->dbid));
711  error_log(__METHOD__ . " " . $this->msg_err);
712  return $this->msg_err;
713  }
714  $this->res = pg_get_result($this->dbid);
715  $err = pg_result_error($this->res);
716  if ($err) {
717  $this->msg_err = ErrorCode::getError('DB0005', $err);
718  $this->err_code = pg_result_error_field($this->res, PGSQL_DIAG_SQLSTATE);
719  }
720 
721  if ($this->msg_err == "") {
722  if (pg_send_execute($this->dbid, '', array()) === false) {
723 
724  $this->msg_err = ErrorCode::getError('DB0007', pg_last_error($this->dbid));
725  $this->setError($sql);
726 
727  return $this->msg_err;
728  }
729  $this->res = pg_get_result($this->dbid);
730  $err = pg_result_error($this->res);
731  if ($err) {
732  $this->msg_err = ErrorCode::getError('DB0002', $err);
733  $this->err_code = pg_result_error_field($this->res, PGSQL_DIAG_SQLSTATE);
734  }
735  }
736  } else {
737  if (pg_send_query($this->dbid, $sql) === false) {
738  $this->msg_err = ErrorCode::getError('DB0008', pg_last_error($this->dbid));
739 
740  $this->setError($sql);
741  return $this->msg_err;
742  }
743  $this->res = pg_get_result($this->dbid);
744  while (pg_get_result($this->dbid)); // purge following queries
745  $err = pg_result_error($this->res);
746  if ($err) {
747  $this->msg_err = ErrorCode::getError('DB0001', $err);
748  $this->err_code = pg_result_error_field($this->res, PGSQL_DIAG_SQLSTATE);
749  }
750  }
751 
752  if ($this->msg_err && ($lvl == 0)) {
753  $orierr = $this->msg_err;
754  try {
755  if ($this->tryCreate()) {
756  // redo the query if create table is done
757  $this->msg_err = $this->exec_query($sql, 1, $prepare);
758  }
759  }
760  catch(Exception $e) {
761  $this->msg_err = $orierr;
762  }
763  }
764  if ($this->msg_err != "") {
765  $this->log->warning("exec_query :" . $sql);
766  $this->log->warning("PostgreSQL Error : " . $this->msg_err);
767  //trigger_error('<pre>'.$this->msg_err."\n".$sql.'</pre>');
768  // throw new Exception($this->msg_err);
769  $this->setError($sql);
770  }
771 
772  if ($SQLDEBUG) {
773  global $TSQLDELAY;
774  $SQLDELAY+= microtime_diff(microtime() , $sqlt1); // to test delay of request
775  $TSQLDELAY[] = array(
776  "t" => sprintf("%.04f", microtime_diff(microtime() , $sqlt1)) ,
777  "s" => str_replace(array(
778  "from",
779  'where'
780  ) , array(
781  "\nfrom",
782  "\nwhere"
783  ) , $sql) ,
784  "st" => getDebugStack(1)
785  );
786  }
787 
788  return ($this->msg_err);
789  }
790  /**
791  * number of return rows after exec_query
792  * @see exec_query
793  * @return int
794  */
795  public function numrows()
796  {
797  if ($this->msg_err == "") {
798  return (pg_num_rows($this->res));
799  } else {
800  return (0);
801  }
802  }
803 
804  public function fetch_array($c, $type = PGSQL_ASSOC)
805  {
806 
807  return (pg_fetch_array($this->res, $c, $type));
808  }
809 
810  public function update()
811  {
812  $err = ErrorCode::getError('DB0009', $this->dbtable);
813 
814  return $err;
815  }
816 
817  public function setError($moreerr = '')
818  {
819  if ($moreerr == '') $err = $this->msg_err;
820  $err = $this->msg_err . "\n" . $moreerr . "\n";
821 
822  if (self::$sqlStrict) {
823  throw new Dcp\Db\Exception($err);
824  }
825 
826  logDebugStack(2, $err);
827  }
828  /**
829  * @deprecated not used now
830  * @return string
831  */
832  public function autoUpdate()
833  {
835  print (" - need update table " . $this->dbtable);
836  $this->log->error("need Update table " . $this->dbtable);
837 
838  $this->log->info("Update table " . $this->dbtable);
839  // need to exec altering queries
840  $objupdate = new DbObj($this->dbaccess);
841  // ------------------------------
842  // first : save table to updated
843  $dumpfile = uniqid(getTmpDir() . "/" . $this->dbtable);
844  $err = $objupdate->exec_query("COPY " . $this->dbtable . " TO '" . $dumpfile . "'");
845  $this->log->info("Dump table " . $this->dbtable . " in " . $dumpfile);
846 
847  if ($err != "") return ($err);
848  // ------------------------------
849  // second : rename table to save data
850  //$err = $objupdate-> exec_query("CREATE TABLE ".$this->dbtable."_old ( ) INHERITS (".$this->dbtable.")",1);
851  //$err = $objupdate-> exec_query("COPY ".$this->dbtable."_old FROM '".$dumpfile."'", 1 );
852  $err = $objupdate->exec_query("ALTER TABLE " . $this->dbtable . " RENAME TO " . $this->dbtable . "_old", 1);
853 
854  if ($err != "") return ($err);
855  // remove index : will be recreated in the following step (create)
856  $err = $this->exec_query("select indexname from pg_indexes where tablename='" . $this->dbtable . "_old'", 1);
857  $nbidx = $this->numrows();
858  for ($c = 0; $c < $nbidx; $c++) {
859 
860  $row = $this->fetch_array($c, PGSQL_ASSOC);
861  $err = $objupdate->exec_query("DROP INDEX " . $row["indexname"], 1);
862  }
863  // --------------------------------------------
864  // third : Create new table with new attributes
865  $this->Create(true);
866  // ---------------------------------------------------
867  // 4th : copy compatible data from old table to new table
868  $first = true;
869  $fields = '';
870  $this->exec_query("SELECT * FROM " . $this->dbtable . "_old");
871  $nbold = $this->numrows();
872  for ($c = 0; $c < $nbold; $c++) {
873 
874  $row = $this->fetch_array($c, PGSQL_ASSOC);
875 
876  if ($first) {
877  // compute compatible fields
878  $inter_fields = array_intersect(array_keys($row) , $this->fields);
879  reset($this->fields);
880  $fields = "(";
881  foreach ($inter_fields as $k => $v) {
882  $fields.= $v . ",";
883  }
884  $fields = substr($fields, 0, strlen($fields) - 1); // remove last comma
885  $fields.= ")";
886  $first = false;
887  }
888  // compute compatible values
889  $values = "(";
890  reset($inter_fields);
891  foreach ($inter_fields as $k => $v) {
892  $values.= "E'" . pg_escape_string($row[$v]) . "',";
893  }
894  $values = substr($values, 0, strlen($values) - 1); // remove last comma
895  $values.= ")";
896  // copy compatible values
897  $sqlInsert = sprintf("INSERT INTO %s %s VALUES ", $this->dbtable, $fields, $values);
898  $err = $objupdate->exec_query($sqlInsert, 1);
899  if ($err != "") return ($err);
900  }
901  // ---------------------------------------------------
902  // 5th :delete old table (has been saved before - dump file)
903  $err = $objupdate->exec_query("DROP TABLE " . $this->dbtable . "_old", 1);
904 
905  return ($err);
906  }
907  /**
908  * set a database transaction save point
909  * @param string $point point identifier
910  * @throws \Dcp\Exception
911  * @return string error message
912  */
913  public function savePoint($point)
914  {
915  if (!$this->dbid) {
916  $err = sprintf("dbid is null cannot save point %s", $point);
917  error_log(__METHOD__ . ":$err");
918  return $err;
919  }
920  if ($this->debug) error_log('[DBG]' . 'BEFORE' . __METHOD__ . $this->dbid);
921  $err = '';
922 
923  $idbid = intval($this->dbid);
924 
925  if (empty(self::$savepoint[$idbid])) {
926  self::$savepoint[$idbid] = array(
927  $point
928  );
929  $err = $this->exec_query("begin");
930  if ($this->debug) error_log('[DBG]' . __METHOD__ . "add(1) $point");
931  } else {
932  self::$savepoint[$idbid][] = $point;
933  if ($this->debug) error_log('[DBG]' . __METHOD__ . "add(2) $point");
934  }
935 
936  if (!$err) {
937  $err = $this->exec_query(sprintf('savepoint "%s"', pg_escape_string($point)));
938  }
939 
940  if ($this->debug) error_log('[DBG]' . 'AFTER' . __METHOD__ . $idbid . ":$point:" . implode(',', self::$savepoint[$idbid]));
941  if ($err) error_log(__METHOD__ . ":$err");
942  return $err;
943  }
944  /**
945  * Set a database transaction advisory lock
946  *
947  * - A transaction advisory lock can only be used within an existing
948  * transaction. So, a transaction must have been explicitly opened
949  * by a call to DbObj::savePoint() before using DbObj::lockPoint().
950  * - The lock is automatically released when the transaction is
951  * commited or rolled back.
952  *
953  * @param int $exclusiveLock Lock's identifier as a signed integer in the int32 range
954  * (i.e. in the range [-2147483648, 2147483647]).
955  * @param string $exclusiveLockPrefix Lock's prefix string limited up to 4 bytes.
956  * @see Dbobj::savePoint()
957  * @throws \Dcp\Exception DB0010, DB0011, and DB0012
958  * @return string error message
959  */
960  public function lockPoint($exclusiveLock, $exclusiveLockPrefix = '')
961  {
962  if (($exclusiveLock_int32 = \Dcp\Utils\Types::to_int32($exclusiveLock)) === false) {
963  throw new \Dcp\Db\Exception("DB0012", var_export($exclusiveLock, true));
964  }
965  $exclusiveLock = $exclusiveLock_int32;
966  if (!$this->dbid) {
967  $err = sprintf("dbid is null cannot add lock %s-%s", $exclusiveLock, $exclusiveLockPrefix);
968  error_log(__METHOD__ . ":$err");
969  return $err;
970  }
971  $err = '';
972 
973  $idbid = intval($this->dbid);
974  if (empty(self::$savepoint[$idbid])) {
975  throw new \Dcp\Db\Exception("DB0011", $exclusiveLock, $exclusiveLockPrefix);
976  }
977 
978  if ($exclusiveLockPrefix) {
979  if (strlen($exclusiveLockPrefix) > 4) {
980  throw new \Dcp\Db\Exception("DB0010", $exclusiveLockPrefix);
981  }
982  $prefixLockId = unpack("i", str_pad($exclusiveLockPrefix, 4)) [1];
983  } else {
984  $prefixLockId = 0;
985  }
986  if (self::$masterLock === false) {
987  $err = $this->exec_query(sprintf('select pg_advisory_lock(0), pg_advisory_unlock(0), pg_advisory_xact_lock(%d,%d);', $exclusiveLock, $prefixLockId));
988  }
989  if ($err) {
990  return $err;
991  }
992  self::$lockpoint[$idbid][sprintf("%d-%s", $exclusiveLock, $exclusiveLockPrefix) ] = array(
993  $exclusiveLock,
994  $prefixLockId
995  );
996 
997  return $err;
998  }
999  /**
1000  * set a database master lock
1001  * the lock is free when explicit call with false parameter.
1002  * When a master lock is set,
1003  * @param bool $useLock set lock (true) or unlock (false)
1004  * @see Dbobj::lockPoint()
1005  * @return string error message
1006  */
1007  public function setMasterLock($useLock)
1008  {
1009  if (!$this->dbid) {
1010  $err = sprintf("dbid is null cannot add master lock ");
1011  error_log(__METHOD__ . ":$err");
1012  return $err;
1013  }
1014 
1015  if ($useLock) {
1016  $err = $this->exec_query('select pg_advisory_lock(0)');
1017  } else {
1018  $err = $this->exec_query('select pg_advisory_unlock(0)');
1019  }
1020  if ($err) {
1021  return $err;
1022  }
1023  self::$masterLock = (bool)$useLock;
1024  return '';
1025  }
1026  /**
1027  * revert to transaction save point
1028  * @param string $point revert point
1029  * @return string error message
1030  */
1031  public function rollbackPoint($point)
1032  {
1033  if (!$this->dbid) {
1034  $err = sprintf("dbid is null cannot save point %s", $point);
1035  error_log(__METHOD__ . ":$err");
1036  return $err;
1037  }
1038  $idbid = intval($this->dbid);
1039  if (isset(self::$savepoint[$idbid])) $lastPoint = array_search($point, self::$savepoint[$idbid]);
1040  else $lastPoint = false;
1041  if ($lastPoint !== false) {
1042 
1043  self::$savepoint[$idbid] = array_slice(self::$savepoint[$idbid], 0, $lastPoint);
1044  $err = $this->exec_query(sprintf('rollback to savepoint "%s"', pg_escape_string($point)));
1045  if ((!$err) && (count(self::$savepoint[$idbid]) == 0)) {
1046  $err = $this->exec_query("commit");
1047  }
1048  } else {
1049  $err = sprintf("cannot rollback unsaved point : %s", $point);
1050  }
1051 
1052  if ($this->debug) error_log('[DBG]' . __METHOD__ . ":$point:" . implode(',', self::$savepoint[$idbid]));
1053  if ($err) error_log(__METHOD__ . ":$err");
1054  return $err;
1055  }
1056  /**
1057  * commit transaction save point
1058  * @param string $point
1059  * @return string error message
1060  */
1061  public function commitPoint($point)
1062  {
1063  if (!$this->dbid) {
1064  $err = sprintf("dbid is null cannot save point %s", $point);
1065  error_log(__METHOD__ . ":$err");
1066  return $err;
1067  }
1068  $idbid = intval($this->dbid);
1069  if ($this->debug) error_log('[DBG]' . __METHOD__ . ":$point:" . implode(',', self::$savepoint[$idbid]));
1070 
1071  $lastPoint = array_search($point, self::$savepoint[$idbid]);
1072 
1073  if ($lastPoint !== false) {
1074  self::$savepoint[$idbid] = array_slice(self::$savepoint[$idbid], 0, $lastPoint);
1075  $err = $this->exec_query(sprintf('release savepoint "%s"', pg_escape_string($point)));
1076  if ((!$err) && (count(self::$savepoint[$idbid]) == 0)) {
1077  $err = $this->exec_query("commit");
1078  }
1079  } else {
1080  $err = sprintf("cannot commit unsaved point : %s", $point);
1081  }
1082  if ($err) error_log(__METHOD__ . ":$err");
1083  return $err;
1084  }
1085  // FIN DE CLASSE
1086 
1087 }
logDebugStack($slice=1, $msg="")
Definition: Lib.Common.php:340
preDelete()
global $SQLDEBUG
Definition: indexq.php:28
autoUpdate()
preInsert()
exec_query($sql, $lvl=0, $prepare=false)
static $lockpoint
Definition: Class.DbObj.php:76
Add($nopost=false, $nopre=false)
$ret
Select($id)
postInsert()
static getError($code, $args=null)
Definition: ErrorCode.php:27
isAffected()
global $TSQLDELAY
Definition: indexq.php:29
$CLASS_DBOBJ_PHP
Definition: Class.DbObj.php:21
postUpdate()
Adds(&$tcopy, $nopost=false)
fetch_array($c, $type=PGSQL_ASSOC)
__construct($dbaccess= '', $id= '', $res= '', $dbid=0)
tryCreate()
global $SQLDELAY
Definition: indexq.php:28
savePoint($point)
getValues()
modify($nopost=false, $sfields="", $nopre=false)
preSelect($id)
lockPoint($exclusiveLock, $exclusiveLockPrefix= '')
static $savepoint
Definition: Class.DbObj.php:75
postInit()
lw($prop)
getParam($name, $def="")
must be in core or global type
Definition: Lib.Common.php:193
getDbid($dbaccess)
Definition: Lib.Common.php:353
microtime_diff($a, $b)
Definition: Lib.Common.php:302
if($updateExistingTable) $point
Definition: updateclass.php:88
getDebugStack($slice=1)
Definition: Lib.Common.php:325
print
Definition: checklist.php:49
getTmpDir($def= '/tmp')
Definition: Lib.Common.php:150
CloseConnect()
init_dbid()
Create($nopost=false)
getDbAccess()
Definition: Lib.Common.php:368
rollbackPoint($point)
commitPoint($point)
setMasterLock($useLock)
postDelete()
static $sqlStrict
Definition: Class.DbObj.php:78
affectColumn($fields, $reset=true)
preUpdate()
if($file) if($subject==""&&$file) if($subject=="") $err
Complete()
affect($array, $more=false, $reset=true)
$fulltextfields
Definition: Class.DbObj.php:63
postSelect($id)
setError($moreerr= '')
← centre documentaire © anakeen