P4A 3 Framework: helper per sincronizzare le tabelle sequence in MySQL

P4A 3 LogoChi utilizza l’RDBMS MySQL in P4A avrà certamente notato che il framework produce in certi casi, delle tabelle il cui nome termina in ‘_seq’. Queste tabelle servono ad avere una compatibilità nella gestione di tutti gli RDBMS supportati (MySQL, PostgreSQL, SQLite, Oracle). PostgreSQL e Oracle, non hanno lo stesso tipo di gestione delle chiavi primarie con l’attributo ‘AUTOINCREMENT‘, rispetto a MySQL e SQLite. La soluzione adottata è di costruire, per ogni tabella con chiave primaria di tipo ‘AUTOINCREMENT’, delle tabelle accessorie che hanno un campo ‘id’ di tipo ‘AUTOINCREMENT’ e di riferirsi a quest’ultime per ottenre il ‘LAST INSERT ID‘. In questo modo viene ricalcato il comportamento delle chiavi primarie di tipo ‘SERIAL’ in PostgreSQL, e anche quelle di Oracle (leggermente diverse).

Prima di tutto una precisazione: questo articolo è riferito esclusivamente agli utenti MySQL.
Se si pensa di utilizzare esclusivamente P4A Framework per la gestione dei dati del nostro database, sarebbe bene in fase di progettazione evitare di usare gli AUTOINCREMENT perché, come detto, non necessari. Nel caso in cui il framework venga utilizzato come ‘backoffice’ per la gestione di dati che vengono alimentati da un form di un sito web, oppure si abbia la necessità di utilizzare una struttura dati pre-esistente con tanto di chiavi primarie ‘AUTOINCREMENT’, potremmo prima o poi trovarci di fronte ad un problema di disallineamento dei valori fra la chiave primaria della tabella e l’id’ della tabella ‘_seq’ associata.

Immaginiamo di avere una tabella “test” con questi due campi:

+----+---------+
| id | name    |
+----+---------+

con ‘id’ chiave primaria, P4A creerà automaticamente una tabella “test_id_seq” con una campo ‘id’ chiave primaria e ‘AUTOINCREMENT’, ed userà i valori forniti da quest’ultima sia per l’inserimento di nuovi record nella tabella “test“, che per altre operazioni sulla chiave primaria. Dunque utilizzando un programma esterno al framework per fare degli inserimenti e volendo mantenere allineate le tabelle principali e le tabelle “_seq” sarà necessario, per ogni “INSERT” nella tabella principale, fare altrettanto nella “_seq”!
Anche utilizzando questo metodo, potrebbe verificarsi prima o poi il problema di un disallineamento, in questo caso ho pensato potesse utile scrivere un metodo della classe “P4A_DB_SOURCE” da utilizzare in extremis, per risistemare le cose.

Per maggiori informazioni sull’utilizzo degli ‘helper’ in P4A suggerisco questo link

Ecco il codice dell’helper, in pratica viene prelevato il valore massimo della chiave primaria ‘AUTOINCREMENT’ della tabella che funge da sorgente dati, quindi viene eliminata la tabella ‘_seq’ associata e successivamente ricostruita con il valore corretto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 function p4a_db_source_resync_seq($source)
 
 { 
	$table = $source->getTable();
	$pKField = $source->getPk();
	$seqTable =  $table."_".$pKField."_seq";
	$lastId = P4A_DB::singleton()->fetchOne("SELECT MAX($pKField) FROM $table");
	$query = "DROP TABLE IF EXISTS `$seqTable`";
	$resQ1 = P4A_DB::singleton()->query($query);
	$query = "CREATE TABLE `$seqTable` (
	`id` int(11) NOT NULL auto_increment,
	PRIMARY KEY  (`id`)
	) TYPE=MyISAM AUTO_INCREMENT=$lastId";
 
	$resQ2 = P4A_DB::singleton()->query($query);
	$query = "INSERT INTO `$seqTable` VALUES ($lastId)";
	$resQ3 = P4A_DB::singleton()->query($query);
  return ($resQ1 && $resQ2 && $resQ3); 
 
 }

Questo codice deve essere inserito in un file con il nome: “p4a_db_source_resync_seq.php” che deve essere posizionato nella directory “libraries” della nostra applicazione P4A.

Ecco infine un po’ di codice di esempio da aggiungere alla nostra ipotetica maschera di manutenzione del db:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class test extends P4A_Base_Mask
{
	public function __construct()
 
	{
		parent::__construct();
 
		// DB Source
		$this->build("p4a_db_source", "source")
			->setTable("test")
			->setPk("id")
			->load();
		$this->setSource($this->source);
 
		// ....
 
	    $this->build("p4a_button", "btn_resync")
			->setLabel("resync")
			->implement("onclick", $this, "resync_seq_table");
 
		// ....
	}
 
	public function resync_seq_table()
	{
		$res = $this->source->resync_seq();	
	}
 
	// ....
}

Conclusioni

Questo metodo dovrebbe essere usato con una certa cautela, e solo come procedura di emergenza. Non consiglio l’utilizzo se c’è la possibilità di accesso in scrittura alla tabella coinvolta da parte di altri utenti.

Riferimenti ed approfondimenti: