<html>
<head>
   <title>Contas bancarias</title>
</head>

<?php
    include 'db.inc';
    include 'error.inc';

    /* executa um comando sql */
    function db_execute($sql)
    {
       global $db;
       return pg_query($db,$sql);
    }

    /* lista todas as contas */
    function db_lista_contas()
    {
      $sql = "SELECT * FROM conta ORDER BY numero";
      $result = db_execute($sql);
      $nrows = pg_numrows($result);

      printf("<pre>\n");
      printf("<b>numero     nome      saldo</b>\n");
      for($i=0; $i<$nrows; $i++)
      {
         $tuple = pg_fetch_array($result,$i); 
         printf("%s %10s %10s\n", 
                 $tuple['numero'], $tuple['nome'], $tuple['saldo']);
      }
      printf("</pre>\n");
    } 

    /* retorna o saldo da conta $numconta */
    function db_saldo( $numconta ) 
    {
      $sql = "SELECT saldo FROM conta WHERE numero=$numconta";
      $result = db_execute($sql);
      return pg_result($result,0,0);
    }

    /* retorna o saldo da conta $numconta e faz um 'lock' a linha */
    function db_saldo_for_update( $numconta ) 
    {
      $sql = "SELECT saldo FROM conta WHERE numero=$numconta FOR UPDATE";
      $result = db_execute($sql);
      return pg_result($result,0,0);
    }

    /* retorna true se $numconta existe. retorna false caso contrario */
    function db_existe_conta( $numconta ) 
    {
      $sql = "SELECT saldo FROM conta WHERE numero=$numconta";
      $result = db_execute($sql);
      $nrows = pg_numrows($result);
      if( $nrows < 1 )
        return false;
      else
        return true;
    }

    /* transfere $quantidade da $conta1 para $conta2.
       retorna 0 se a operacao tiver de ser repetida devido 
                 a problemas com a serializacao da transaccao.
       retorna 1 se a operação tiver sucesso.
       retorna 2 se a $conta1 não tem saldo suficiente.
    */
    function db_transfere($conta1,$conta2,$quantidade)
    {
      error_reporting(0);
      /* começa a transaccao */
      db_execute("BEGIN WORK");
      db_execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");

      /* verifica se existe dinheiro suficiente na conta 1 */
      $saldo = db_saldo($conta1);
      if( $saldo >= $quantidade ) {
         /* transfere o dinheiro */
         if(db_execute("UPDATE conta SET saldo=saldo+$quantidade 
                      WHERE numero=$conta2"))
           {
             sleep(5);  // so para poder testar a concorrencia...
             if(db_execute("UPDATE conta SET saldo=saldo-$quantidade 
                          WHERE numero=$conta1"))
               {
                 db_execute("COMMIT WORK");
                 return 1;
               }
             else 
               {
                 db_execute("ROLLBACK WORK");
                 return 0;
               }   
           }
         else 
           {
             db_execute("ROLLBACK WORK");
             return 0;
           }   
      }
      else {
         db_execute("ROLLBACK WORK");
         return 2;
      }
    }
?>


<h1>Tabela 'conta'</h1>

<?php
    $db = dbconnect($connection_string);

    $numConta1 = $_POST["numConta1"];
    $numConta2 = $_POST["numConta2"];
    $quantidade = $_POST["quantidade"];

    if( isset($numConta1) && isset($numConta2) && isset($quantidade)) {
      // verifica que $quantidade não é negativo
      if($quantidade<0) 
	printf("<p>ERRO: quantidade negativa!</p>\n");
      // verifica que as contas existem
      if(!db_existe_conta($numConta1))
	printf("<p>ERRO: Conta $numConta1 nao existe!</p>\n");
      if(!db_existe_conta($numConta2)) 
	printf("<p>ERRO: Conta $numConta2 nao existe!</p>\n");
      // efectua a transaccao 
      do {
         $result =  db_transfere($numConta1,$numConta2,$quantidade);
      } while( $result==0 );
      if( $result==1 )
        printf("<p>Foi transferido %d euros da conta %d para a conta %d</p>\n",
               $quantidade, $numConta1, $numConta2);
      else
	printf("<p>Saldo insuficiente na conta $numConta1!</p>\n");

    }
    db_lista_contas();
?>

<hr>

<h1>Transferencia de dinheiro</h1>
<form action="<?php echo $PHP_SELF; ?>" method="post">
<table border=0>
  <tr>
     <td align=right>Numero da conta 1:</td>
     <td align=left><input type="text" name="numConta1" size="15"></td>
  </tr>
  <tr>
     <td align=right>Numero da conta 2:</td>
     <td align=left><input type="text" name="numConta2" size="15"></td>
  </tr>
  <tr>
     <td align=right>Quantidade:</td>
     <td align=left><input type="text" name="quantidade" size="15"></td>
  </tr>
  <tr>
     <td align=left><input type="submit" value="transferir"></td>
  </tr>
</table>
</form>  

<?php
    pg_close($db);
?>

</body>
</html>