1 <?php
  2   3   4   5   6   7   8 
  9 
 10 defined('JPATH_PLATFORM') or die;
 11 
 12  13  14  15  16 
 17 class JDatabaseImporterPostgresql extends JDatabaseImporter
 18 {
 19      20  21  22  23  24  25  26 
 27     public function check()
 28     {
 29         
 30         if (!($this->db instanceof JDatabaseDriverPostgresql))
 31         {
 32             throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
 33         }
 34 
 35         
 36         if (empty($this->from))
 37         {
 38             throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
 39         }
 40 
 41         return $this;
 42     }
 43 
 44      45  46  47  48  49  50  51  52  53 
 54     protected function getAddColumnSql($table, SimpleXMLElement $field)
 55     {
 56         return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSql($field);
 57     }
 58 
 59      60  61  62  63  64  65  66  67 
 68     protected function getAddIndexSql(SimpleXMLElement $field)
 69     {
 70         return (string) $field['Query'];
 71     }
 72 
 73      74  75  76  77  78  79  80  81 
 82     protected function getAlterTableSql(SimpleXMLElement $structure)
 83     {
 84         $table = $this->getRealTableName($structure['name']);
 85         $oldFields = $this->db->getTableColumns($table);
 86         $oldKeys = $this->db->getTableKeys($table);
 87         $oldSequence = $this->db->getTableSequences($table);
 88         $alters = array();
 89 
 90         
 91         $newFields = $structure->xpath('field');
 92         $newKeys = $structure->xpath('key');
 93         $newSequence = $structure->xpath('sequence');
 94 
 95         
 96         $oldSeq = $this->getSeqLookup($oldSequence);
 97         $newSequenceLook = $this->getSeqLookup($newSequence);
 98 
 99         foreach ($newSequenceLook as $kSeqName => $vSeq)
100         {
101             if (isset($oldSeq[$kSeqName]))
102             {
103                 
104                 $column = $oldSeq[$kSeqName][0];
105 
106                 
107                 if (version_compare($this->db->getVersion(), '9.1.0') < 0)
108                 {
109                     $column->Min_Value = '1';
110                     $column->Max_Value = '9223372036854775807';
111                     $column->Increment = '1';
112                     $column->Cycle_option = 'NO';
113                     $column->Start_Value = '1';
114                 }
115 
116                 
117                 $change = ((string) $vSeq[0]['Type'] != $column->Type) || ((string) $vSeq[0]['Start_Value'] != $column->Start_Value)
118                     || ((string) $vSeq[0]['Min_Value'] != $column->Min_Value) || ((string) $vSeq[0]['Max_Value'] != $column->Max_Value)
119                     || ((string) $vSeq[0]['Increment'] != $column->Increment) || ((string) $vSeq[0]['Cycle_option'] != $column->Cycle_option)
120                     || ((string) $vSeq[0]['Table'] != $column->Table) || ((string) $vSeq[0]['Column'] != $column->Column)
121                     || ((string) $vSeq[0]['Schema'] != $column->Schema) || ((string) $vSeq[0]['Name'] != $column->Name);
122 
123                 if ($change)
124                 {
125                     $alters[] = $this->getChangeSequenceSql($kSeqName, $vSeq);
126                 }
127 
128                 
129                 unset($oldSeq[$kSeqName]);
130             }
131             else
132             {
133                 
134                 $alters[] = $this->getAddSequenceSql($newSequenceLook[$kSeqName][0]);
135             }
136         }
137 
138         
139         foreach ($oldSeq as $name => $column)
140         {
141             
142             $alters[] = $this->getDropSequenceSql($name);
143         }
144 
145         
146         
147         foreach ($newFields as $field)
148         {
149             $fName = (string) $field['Field'];
150 
151             if (isset($oldFields[$fName]))
152             {
153                 
154                 $column = $oldFields[$fName];
155 
156                 
157                 $change = ((string) $field['Type'] != $column->Type) || ((string) $field['Null'] != $column->Null)
158                     || ((string) $field['Default'] != $column->Default);
159 
160                 if ($change)
161                 {
162                     $alters[] = $this->getChangeColumnSql($table, $field);
163                 }
164 
165                 
166                 unset($oldFields[$fName]);
167             }
168             else
169             {
170                 
171                 $alters[] = $this->getAddColumnSql($table, $field);
172             }
173         }
174 
175         
176         foreach ($oldFields as $name => $column)
177         {
178             
179             $alters[] = $this->getDropColumnSql($table, $name);
180         }
181 
182         
183         
184         $oldLookup = $this->getIdxLookup($oldKeys);
185         $newLookup = $this->getIdxLookup($newKeys);
186 
187         
188         foreach ($newLookup as $name => $keys)
189         {
190             
191             if (isset($oldLookup[$name]))
192             {
193                 $same = true;
194                 $newCount = count($newLookup[$name]);
195                 $oldCount = count($oldLookup[$name]);
196 
197                 
198                 if ($newCount == $oldCount)
199                 {
200                     for ($i = 0; $i < $newCount; $i++)
201                     {
202                         
203                         $same = ((string) $newLookup[$name][$i]['Query'] == $oldLookup[$name][$i]->Query);
204 
205                         if (!$same)
206                         {
207                             
208                             break;
209                         }
210                     }
211                 }
212                 else
213                 {
214                     
215                     $same = false;
216                 }
217 
218                 if (!$same)
219                 {
220                     $alters[] = $this->getDropIndexSql($name);
221                     $alters[]  = (string) $newLookup[$name][0]['Query'];
222                 }
223 
224                 
225                 unset($oldLookup[$name]);
226             }
227             else
228             {
229                 
230                 $alters[] = (string) $newLookup[$name][0]['Query'];
231             }
232         }
233 
234         
235         foreach ($oldLookup as $name => $keys)
236         {
237             if ($oldLookup[$name][0]->is_primary == 'TRUE')
238             {
239                 $alters[] = $this->getDropPrimaryKeySql($table, $oldLookup[$name][0]->Index);
240             }
241             else
242             {
243                 $alters[] = $this->getDropIndexSql($name);
244             }
245         }
246 
247         return $alters;
248     }
249 
250     251 252 253 254 255 256 257 258 
259     protected function getDropSequenceSql($name)
260     {
261         return 'DROP SEQUENCE ' . $this->db->quoteName($name);
262     }
263 
264     265 266 267 268 269 270 271 272 
273     protected function getAddSequenceSql($field)
274     {
275         
276         if (version_compare($this->db->getVersion(), '9.1.0') < 0)
277         {
278             $field['Min_Value'] = '1';
279             $field['Max_Value'] = '9223372036854775807';
280             $field['Increment'] = '1';
281             $field['Cycle_option'] = 'NO';
282             $field['Start_Value'] = '1';
283         }
284 
285         return 'CREATE SEQUENCE ' . (string) $field['Name'] .
286             ' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . $field['Min_Value'] .
287             ' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value'] .
288             (((string) $field['Cycle_option'] == 'NO') ? ' NO' : '') . ' CYCLE' .
289             ' OWNED BY ' . $this->db->quoteName((string) $field['Schema'] . '.' . (string) $field['Table'] . '.' . (string) $field['Column']);
290     }
291 
292     293 294 295 296 297 298 299 300 
301     protected function getChangeSequenceSql($field)
302     {
303         
304         if (version_compare($this->db->getVersion(), '9.1.0') < 0)
305         {
306             $field['Min_Value'] = '1';
307             $field['Max_Value'] = '9223372036854775807';
308             $field['Increment'] = '1';
309             $field['Cycle_option'] = 'NO';
310             $field['Start_Value'] = '1';
311         }
312 
313         return 'ALTER SEQUENCE ' . (string) $field['Name'] .
314             ' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . (string) $field['Min_Value'] .
315             ' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value'] .
316             ' OWNED BY ' . $this->db->quoteName((string) $field['Schema'] . '.' . (string) $field['Table'] . '.' . (string) $field['Column']);
317     }
318 
319     320 321 322 323 324 325 326 327 328 
329     protected function getChangeColumnSql($table, SimpleXMLElement $field)
330     {
331         return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ALTER COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' '
332             . $this->getAlterColumnSql($table, $field);
333     }
334 
335     336 337 338 339 340 341 342 343 344 
345     protected function getAlterColumnSql($table, $field)
346     {
347         
348         $blobs = array('text', 'smalltext', 'mediumtext', 'largetext');
349 
350         $fName = (string) $field['Field'];
351         $fType = (string) $field['Type'];
352         $fNull = (string) $field['Null'];
353         $fDefault = (isset($field['Default']) && $field['Default'] != 'NULL') ?
354                         preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default'])
355                     : null;
356 
357         $query = ' TYPE ' . $fType;
358 
359         if ($fNull == 'NO')
360         {
361             if (in_array($fType, $blobs) || $fDefault === null)
362             {
363                 $query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET NOT NULL' .
364                         ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' DROP DEFAULT';
365             }
366             else
367             {
368                 $query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET NOT NULL' .
369                         ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET DEFAULT ' . $fDefault;
370             }
371         }
372         else
373         {
374             if ($fDefault !== null)
375             {
376                 $query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' DROP NOT NULL' .
377                         ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET DEFAULT ' . $fDefault;
378             }
379         }
380 
381         
382         if (strpos($fDefault, 'nextval') !== false)
383         {
384             $query .= ";\nALTER SEQUENCE " . $this->db->quoteName($table . '_' . $fName . '_seq') . ' OWNED BY ' . $this->db->quoteName($table . '.' . $fName);
385         }
386 
387         return $query;
388     }
389 
390     391 392 393 394 395 396 397 398 
399     protected function getColumnSql(SimpleXMLElement $field)
400     {
401         
402         $blobs = array('text', 'smalltext', 'mediumtext', 'largetext');
403 
404         $fName = (string) $field['Field'];
405         $fType = (string) $field['Type'];
406         $fNull = (string) $field['Null'];
407         $fDefault = (isset($field['Default']) && $field['Default'] != 'NULL') ?
408                         preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default'])
409                     : null;
410 
411         
412         if (strpos($fDefault, 'nextval') !== false)
413         {
414             $query = $this->db->quoteName($fName) . ' SERIAL';
415         }
416         else
417         {
418             $query = $this->db->quoteName($fName) . ' ' . $fType;
419 
420             if ($fNull == 'NO')
421             {
422                 if (in_array($fType, $blobs) || $fDefault === null)
423                 {
424                     $query .= ' NOT NULL';
425                 }
426                 else
427                 {
428                     $query .= ' NOT NULL DEFAULT ' . $fDefault;
429                 }
430             }
431             else
432             {
433                 if ($fDefault !== null)
434                 {
435                     $query .= ' DEFAULT ' . $fDefault;
436                 }
437             }
438         }
439 
440         return $query;
441     }
442 
443     444 445 446 447 448 449 450 451 
452     protected function getDropIndexSql($name)
453     {
454         return 'DROP INDEX ' . $this->db->quoteName($name);
455     }
456 
457     458 459 460 461 462 463 464 465 466 
467     protected function getDropPrimaryKeySql($table, $name)
468     {
469         return 'ALTER TABLE ONLY ' . $this->db->quoteName($table) . ' DROP CONSTRAINT ' . $this->db->quoteName($name);
470     }
471 
472     473 474 475 476 477 478 479 480 481 
482     protected function getIdxLookup($keys)
483     {
484         
485         $lookup = array();
486 
487         foreach ($keys as $key)
488         {
489             if ($key instanceof SimpleXMLElement)
490             {
491                 $kName = (string) $key['Index'];
492             }
493             else
494             {
495                 $kName = $key->Index;
496             }
497 
498             if (empty($lookup[$kName]))
499             {
500                 $lookup[$kName] = array();
501             }
502 
503             $lookup[$kName][] = $key;
504         }
505 
506         return $lookup;
507     }
508 
509     510 511 512 513 514 515 516 517 518 
519     protected function getSeqLookup($sequences)
520     {
521         
522         $lookup = array();
523 
524         foreach ($sequences as $seq)
525         {
526             if ($seq instanceof SimpleXMLElement)
527             {
528                 $sName = (string) $seq['Name'];
529             }
530             else
531             {
532                 $sName = $seq->Name;
533             }
534 
535             if (empty($lookup[$sName]))
536             {
537                 $lookup[$sName] = array();
538             }
539 
540             $lookup[$sName][] = $seq;
541         }
542 
543         return $lookup;
544     }
545 }
546