1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.efaps.db.databases;
22
23 import java.sql.Connection;
24 import java.sql.DatabaseMetaData;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.ResultSetMetaData;
28 import java.sql.SQLException;
29 import java.sql.Statement;
30 import java.util.Map;
31
32 import org.apache.commons.dbutils.BasicRowProcessor;
33 import org.apache.commons.dbutils.RowProcessor;
34 import org.efaps.db.databases.information.TableInformation;
35 import org.joda.time.ReadableDateTime;
36 import org.joda.time.format.ISODateTimeFormat;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
41
42
43
44
45
46
47
48 public class PostgreSQLDatabase
49 extends AbstractDatabase<PostgreSQLDatabase>
50 {
51
52
53
54 private static final Logger LOG = LoggerFactory.getLogger(PostgreSQLDatabase.class);
55
56
57
58
59
60
61
62 private static final String SQL_UNIQUE_KEYS = "select "
63 + "a.constraint_name as INDEX_NAME, "
64 + "a.table_name as TABLE_NAME, "
65 + "b.column_name as COLUMN_NAME, "
66 + "b.ordinal_position as ORDINAL_POSITION "
67 + "from "
68 + "information_schema.table_constraints a,"
69 + "information_schema.key_column_usage b "
70 + "where "
71 + "a.constraint_type='UNIQUE' "
72 + "and a.table_schema=b.table_schema "
73 + "and a.table_name=b.table_name "
74 + "and a.constraint_name=b.constraint_name";
75
76
77
78
79
80
81
82 private static final String SQL_FOREIGN_KEYS = "select "
83 + "a.table_name as TABLE_NAME, "
84 + "a.constraint_name as FK_NAME, "
85 + "b.column_name as FKCOLUMN_NAME, "
86 + "case "
87 + "when c.delete_rule='NO ACTION' then '" + DatabaseMetaData.importedKeyNoAction + "' "
88 + "when c.delete_rule='CASCASE' then '" + DatabaseMetaData.importedKeyCascade + "' "
89 + "else '' end as DELETE_RULE, "
90 + "d.table_name as PKTABLE_NAME, "
91 + "d.column_name as PKCOLUMN_NAME "
92 + "from "
93 + "information_schema.table_constraints a, "
94 + "information_schema.constraint_column_usage b, "
95 + "information_schema.referential_constraints c, "
96 + "information_schema.constraint_column_usage d "
97 + "where "
98 + "a.constraint_type='FOREIGN KEY' "
99 + "and a.constraint_name=b.constraint_name "
100 + "and a.constraint_name=c.constraint_name "
101 + "and c.unique_constraint_name=d.constraint_name";
102
103
104
105
106
107
108 private static final RowProcessor ROWPROCESSOR = new BasicRowProcessor()
109 {
110
111
112
113
114
115
116
117
118
119
120
121
122 @Override
123 public Object[] toArray(final ResultSet _rs)
124 throws SQLException
125 {
126 final ResultSetMetaData metaData = _rs.getMetaData();
127 final int cols = metaData.getColumnCount();
128 final Object[] result = new Object[cols];
129
130 for (int i = 0; i < cols; i++) {
131 switch (metaData.getColumnType(i + 1)) {
132 case java.sql.Types.TIMESTAMP:
133 result[i] = _rs.getTimestamp(i + 1);
134 break;
135 default:
136 result[i] = _rs.getObject(i + 1);
137 }
138 }
139 return result;
140 }
141 };
142
143
144
145
146 public PostgreSQLDatabase()
147 {
148 addMapping(ColumnType.INTEGER, "bigint", "null", "int8", "int4", "bigserial");
149 addMapping(ColumnType.DECIMAL, "numeric", "null", "decimal", "numeric");
150 addMapping(ColumnType.REAL, "real", "null", "float4");
151 addMapping(ColumnType.STRING_SHORT, "char", "null", "bpchar");
152 addMapping(ColumnType.STRING_LONG, "varchar", "null", "varchar");
153 addMapping(ColumnType.DATETIME, "timestamp", "null", "timestamp");
154 addMapping(ColumnType.BLOB, "bytea", "null", "bytea");
155 addMapping(ColumnType.CLOB, "text", "null", "text");
156 addMapping(ColumnType.BOOLEAN, "boolean", "null", "bool");
157 }
158
159
160
161
162
163 @Override
164 public boolean isConnected(final Connection _connection)
165 throws SQLException
166 {
167 boolean ret = false;
168 final StringBuilder cmd = new StringBuilder();
169 cmd.append(" SELECT version();");
170 PreparedStatement stmt = null;
171 stmt = _connection.prepareStatement(cmd.toString());
172 try {
173 final ResultSet resultset = stmt.executeQuery();
174 if (resultset.next()) {
175 final String str = resultset.getString(1);
176 ret = str.toUpperCase().contains("POSTGRESQL");
177 }
178 resultset.close();
179 } finally {
180 stmt.close();
181 }
182 return ret;
183 }
184
185
186
187
188 @Override
189 public String getCurrentTimeStamp()
190 {
191 return "current_timestamp";
192 }
193
194
195
196
197 @Override
198 public String getTimestampValue(final String _dateTimeStr)
199 {
200 return "timestamp '" + _dateTimeStr + "'";
201 }
202
203
204
205
206
207
208 @Override
209 public String getStr4DateTime(final ReadableDateTime _value)
210 {
211 String ret;
212 if (_value.getEra() == 0) {
213 ret = _value.toDateTime().minusYears(1).toString(ISODateTimeFormat.dateHourMinuteSecondFraction());
214 ret = ret.substring(1) + " BC";
215 } else {
216 ret = _value.toDateTime().toString(ISODateTimeFormat.dateHourMinuteSecondFraction());
217 }
218 return ret;
219 }
220
221
222
223
224 @Override
225 public Object getBooleanValue(final Boolean _value)
226 {
227 return _value;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 @Override
247 @SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
248 public void deleteAll(final Connection _con)
249 throws SQLException
250 {
251
252 final Statement stmtSel = _con.createStatement();
253 final Statement stmtExec = _con.createStatement();
254
255 try {
256 if (PostgreSQLDatabase.LOG.isInfoEnabled()) {
257 PostgreSQLDatabase.LOG.info("Remove all Tables");
258 }
259
260 final DatabaseMetaData metaData = _con.getMetaData();
261
262
263 final ResultSet rsViews = metaData.getTables(null, null, "%", new String[] { "VIEW" });
264 while (rsViews.next()) {
265 final String viewName = rsViews.getString("TABLE_NAME");
266 if (PostgreSQLDatabase.LOG.isDebugEnabled()) {
267 PostgreSQLDatabase.LOG.debug(" - View '" + viewName + "'");
268 }
269 stmtExec.execute("drop view " + viewName);
270 }
271 rsViews.close();
272
273
274 final ResultSet rsTables = metaData.getTables(null, null, "%", new String[] { "TABLE" });
275 while (rsTables.next()) {
276 final String tableName = rsTables.getString("TABLE_NAME");
277 if (PostgreSQLDatabase.LOG.isDebugEnabled()) {
278 PostgreSQLDatabase.LOG.debug(" - Table '" + tableName + "'");
279 }
280 stmtExec.execute("drop table " + tableName + " cascade");
281 }
282 rsTables.close();
283
284
285 final ResultSet rsSeq = stmtSel.executeQuery("SELECT sequence_name FROM information_schema.sequences");
286 while (rsSeq.next()) {
287 final String seqName = rsSeq.getString("sequence_name");
288 if (PostgreSQLDatabase.LOG.isDebugEnabled()) {
289 PostgreSQLDatabase.LOG.debug(" - Sequence '" + seqName + "'");
290 }
291 stmtExec.execute("drop sequence " + seqName);
292 }
293 rsSeq.close();
294
295 } finally {
296 stmtSel.close();
297 stmtExec.close();
298 }
299 }
300
301
302
303
304 @Override
305 public PostgreSQLDatabase deleteView(final Connection _con,
306 final String _name)
307 throws SQLException
308 {
309 final Statement stmtExec = _con.createStatement();
310 try {
311 stmtExec.execute("drop view " + _name);
312 } finally {
313 stmtExec.close();
314 }
315 return this;
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 @Override
337 public PostgreSQLDatabase createTable(final Connection _con,
338 final String _table)
339 throws SQLException
340 {
341 final Statement stmt = _con.createStatement();
342 try {
343 stmt.executeUpdate(new StringBuilder()
344 .append("create table ").append(_table).append(" (")
345 .append("ID bigint")
346 .append(",").append("constraint ").append(_table).append("_PK_ID primary key (ID)")
347 .append(") without OIDS;")
348 .toString());
349 } finally {
350 stmt.close();
351 }
352
353 return this;
354 }
355
356
357
358
359 @Override
360 public PostgreSQLDatabase defineTableAutoIncrement(final Connection _con,
361 final String _table)
362 throws SQLException
363 {
364 final Statement stmt = _con.createStatement();
365 try {
366
367 stmt.execute(new StringBuilder()
368 .append("create sequence ").append(_table).append("_id_seq")
369 .toString());
370
371 stmt.execute(new StringBuilder()
372 .append("alter table ").append(_table)
373 .append(" alter column id set default nextval('")
374 .append(_table).append("_id_seq')")
375 .toString());
376
377 stmt.execute(new StringBuilder()
378 .append("alter sequence ").append(_table).append("_id_seq owned by ")
379 .append(_table).append(".id")
380 .toString());
381 } finally {
382 stmt.close();
383 }
384 return this;
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399 @Override
400 public long getNewId(final Connection _con,
401 final String _table,
402 final String _column)
403 throws SQLException
404 {
405
406 long ret = 0;
407 final Statement stmt = _con.createStatement();
408
409 try {
410 final StringBuilder cmd = new StringBuilder();
411 cmd.append("select nextval('").append(_table).append("_").append(_column).append("_SEQ')");
412
413 final ResultSet rs = stmt.executeQuery(cmd.toString());
414 if (rs.next()) {
415 ret = rs.getLong(1);
416 }
417 rs.close();
418 } finally {
419 stmt.close();
420 }
421 return ret;
422 }
423
424
425
426
427 @Override
428 public boolean supportsBinaryInputStream()
429 {
430 return true;
431 }
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449 @Override
450 public PostgreSQLDatabase createSequence(final Connection _con,
451 final String _name,
452 final long _startValue)
453 throws SQLException
454 {
455 final long value = _startValue - 1;
456 final StringBuilder cmd = new StringBuilder();
457 cmd.append("CREATE SEQUENCE \"").append(_name.toLowerCase())
458 .append("\" INCREMENT 1")
459 .append(" MINVALUE ").append(value)
460 .append(" MAXVALUE 9223372036854775807 ")
461 .append(" START ").append(value)
462 .append(" CACHE 1;");
463
464 final PreparedStatement stmt = _con.prepareStatement(cmd.toString());
465 try {
466 stmt.execute();
467 } finally {
468 stmt.close();
469 }
470 if (!_con.getAutoCommit()) {
471 _con.commit();
472 }
473
474 nextSequence(_con, _name);
475 return this;
476 }
477
478
479
480
481 @Override
482 public PostgreSQLDatabase deleteSequence(final Connection _con,
483 final String _name)
484 throws SQLException
485 {
486 final String cmd = new StringBuilder()
487 .append("DROP SEQUENCE \"").append(_name.toLowerCase()).append("\" RESTRICT")
488 .toString();
489 final Statement stmt = _con.createStatement();
490 try {
491 stmt.executeUpdate(cmd);
492 } finally {
493 stmt.close();
494 }
495 return this;
496 }
497
498
499
500
501
502
503
504
505
506
507
508
509 @Override
510 @SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
511 public boolean existsSequence(final Connection _con,
512 final String _name)
513 throws SQLException
514 {
515 final boolean ret;
516 final String cmd = new StringBuilder()
517 .append("SELECT relname FROM pg_class WHERE relkind = 'S' AND relname='")
518 .append(_name.toLowerCase()).append("'")
519 .toString();
520 final Statement stmt = _con.createStatement();
521 try {
522 final ResultSet resultset = stmt.executeQuery(cmd);
523 ret = resultset.next();
524 resultset.close();
525 } finally {
526 stmt.close();
527 }
528 return ret;
529 }
530
531
532
533
534 @Override
535 public long nextSequence(final Connection _con,
536 final String _name)
537 throws SQLException
538 {
539 final long ret;
540 final String cmd = new StringBuilder()
541 .append("SELECT NEXTVAL('\"" + _name.toLowerCase() + "\"') ")
542 .toString();
543 final Statement stmt = _con.createStatement();
544 try {
545 final ResultSet resultset = stmt.executeQuery(cmd);
546 if (resultset.next()) {
547 ret = resultset.getLong(1);
548 } else {
549 throw new SQLException("fetching new value from sequence '" + _name + "' failed");
550 }
551 resultset.close();
552 } finally {
553 stmt.close();
554 }
555 return ret;
556 }
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575 @Override
576 public PostgreSQLDatabase setSequence(final Connection _con,
577 final String _name,
578 final long _value)
579 throws SQLException
580 {
581 deleteSequence(_con, _name);
582 createSequence(_con, _name, _value);
583 return this;
584 }
585
586
587
588
589 @Override
590 public String getHibernateDialect()
591 {
592 return "org.hibernate.dialect.PostgreSQL82Dialect";
593 }
594
595
596
597
598
599
600
601
602
603
604
605
606
607 @Override
608 protected void initTableInfoUniqueKeys(final Connection _con,
609 final String _sql,
610 final Map<String, TableInformation> _cache4Name)
611 throws SQLException
612 {
613 super.initTableInfoUniqueKeys(_con, PostgreSQLDatabase.SQL_UNIQUE_KEYS, _cache4Name);
614 }
615
616
617
618
619
620
621
622
623
624
625
626
627
628 @Override
629 protected void initTableInfoForeignKeys(final Connection _con,
630 final String _sql,
631 final Map<String, TableInformation> _cache4Name)
632 throws SQLException
633 {
634 super.initTableInfoForeignKeys(_con, PostgreSQLDatabase.SQL_FOREIGN_KEYS, _cache4Name);
635 }
636
637
638
639
640 @Override
641 protected StringBuilder getAlterColumn(final String _columnName,
642 final org.efaps.db.databases.AbstractDatabase.ColumnType _columnType)
643 {
644 final StringBuilder ret = new StringBuilder()
645 .append(" alter ").append(getColumnQuote()).append(_columnName).append(getColumnQuote())
646 .append(" type ")
647 .append(getWriteSQLTypeName(_columnType));
648 return ret;
649 }
650
651
652
653
654 @Override
655 protected StringBuilder getAlterColumnIsNotNull(final String _columnName,
656 final boolean _isNotNull)
657 {
658 final StringBuilder ret = new StringBuilder()
659 .append(" alter column ").append(getColumnQuote()).append(_columnName).append(getColumnQuote());
660 if (_isNotNull) {
661 ret.append(" set ");
662 } else {
663 ret.append(" drop ");
664 }
665 ret.append(" not null");
666 return ret;
667 }
668
669
670
671
672 @Override
673 protected boolean check4NullValues(final Connection _con,
674 final String _tableName,
675 final String _columnName)
676 throws SQLException
677 {
678 boolean ret = true;
679 final StringBuilder cmd = new StringBuilder();
680 cmd.append("select count(*) from ").append(getTableQuote()).append(_tableName).append(getTableQuote())
681 .append(" where ").append(getColumnQuote()).append(_columnName).append(getColumnQuote())
682 .append(" is null");
683
684 PostgreSQLDatabase.LOG.debug(" ..SQL> {}", cmd);
685
686 final Statement stmt = _con.createStatement();
687 ResultSet rs = null;
688 try {
689 rs = stmt.executeQuery(cmd.toString());
690 rs.next();
691 ret = rs.getInt(1) > 0;
692 } finally {
693 if (rs != null) {
694 rs.close();
695 }
696 stmt.close();
697 }
698 return ret;
699 }
700
701
702
703
704 @Override
705 public RowProcessor getRowProcessor()
706 {
707 return PostgreSQLDatabase.ROWPROCESSOR;
708 }
709 }