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