1 /**
2  * HibernateD - Object-Relation Mapping for D programming language, with interface similar to Hibernate. 
3  * 
4  * Source file hibernated/dialects/sqlitedialect.d.
5  *
6  * This module contains implementation of PGSQLDialect class which provides implementation specific SQL syntax information.
7  * 
8  * Copyright: Copyright 2013
9  * License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10  * Author:   Vadim Lopatin
11  */
12 module hibernated.dialects.pgsqldialect;
13 
14 import std.conv;
15 
16 import hibernated.dialect;
17 import hibernated.metadata;
18 import hibernated.type;
19 import ddbc.core;
20 
21 
22 string[] PGSQL_RESERVED_WORDS = 
23     [
24      "ABORT",
25      "ACTION",
26      "ADD",
27      "AFTER",
28      "ALL",
29      "ALTER",
30      "ANALYZE",
31      "AND",
32      "AS",
33      "ASC",
34      "ATTACH",
35      "AUTOINCREMENT",
36      "BEFORE",
37      "BEGIN",
38      "BETWEEN",
39      "BY",
40      "CASCADE",
41      "CASE",
42      "CAST",
43      "CHECK",
44      "COLLATE",
45      "COLUMN",
46      "COMMIT",
47      "CONFLICT",
48      "CONSTRAINT",
49      "CREATE",
50      "CROSS",
51      "CURRENT_DATE",
52      "CURRENT_TIME",
53      "CURRENT_TIMESTAMP",
54      "DATABASE",
55      "DEFAULT",
56      "DEFERRABLE",
57      "DEFERRED",
58      "DELETE",
59      "DESC",
60      "DETACH",
61      "DISTINCT",
62      "DROP",
63      "EACH",
64      "ELSE",
65      "END",
66      "ESCAPE",
67      "EXCEPT",
68      "EXCLUSIVE",
69      "EXISTS",
70      "EXPLAIN",
71      "FAIL",
72      "FOR",
73      "FOREIGN",
74      "FROM",
75      "FULL",
76      "GLOB",
77      "GROUP",
78      "HAVING",
79      "IF",
80      "IGNORE",
81      "IMMEDIATE",
82      "IN",
83      "INDEX",
84      "INDEXED",
85      "INITIALLY",
86      "INNER",
87      "INSERT",
88      "INSTEAD",
89      "INTERSECT",
90      "INTO",
91      "IS",
92      "ISNULL",
93      "JOIN",
94      "KEY",
95      "LEFT",
96      "LIKE",
97      "LIMIT",
98      "MATCH",
99      "NATURAL",
100      "NO",
101      "NOT",
102      "NOTNULL",
103      "NULL",
104      "OF",
105      "OFFSET",
106      "ON",
107      "OR",
108      "ORDER",
109      "OUTER",
110      "PLAN",
111      "PRAGMA",
112      "PRIMARY",
113      "QUERY",
114      "RAISE",
115      "REFERENCES",
116      "REGEXP",
117      "REINDEX",
118      "RELEASE",
119      "RENAME",
120      "REPLACE",
121      "RESTRICT",
122      "RIGHT",
123      "ROLLBACK",
124      "ROW",
125      "SAVEPOINT",
126      "SELECT",
127      "SET",
128      "TABLE",
129      "TEMP",
130      "TEMPORARY",
131      "THEN",
132      "TO",
133      "TRANSACTION",
134      "TRIGGER",
135      "UNION",
136      "UNIQUE",
137      "UPDATE",
138      "USING",
139      "VACUUM",
140      "VALUES",
141      "VIEW",
142      "VIRTUAL",
143      "WHEN",
144      "WHERE",
145      ];
146 
147 
148 class PGSQLDialect : Dialect {
149     ///The character specific to this dialect used to close a quoted identifier.
150     override char closeQuote() const { return '`'; }
151     ///The character specific to this dialect used to begin a quoted identifier.
152     override char  openQuote() const { return '`'; }
153     
154     // returns string like "BIGINT(20) NOT NULL" or "VARCHAR(255) NULL"
155     override string getColumnTypeDefinition(const PropertyInfo pi, const PropertyInfo overrideTypeFrom = null) {
156         immutable Type type = overrideTypeFrom !is null ? overrideTypeFrom.columnType : pi.columnType;
157         immutable SqlType sqlType = type.getSqlType();
158         bool fk = pi is null;
159         string nullablility = !fk && pi.nullable ? " NULL" : " NOT NULL";
160         string pk = !fk && pi.key ? " PRIMARY KEY" : "";
161         if (!fk && pi.generated) {
162             if (sqlType == SqlType.SMALLINT || sqlType == SqlType.TINYINT)
163                 return "SERIAL PRIMARY KEY";
164             if (sqlType == SqlType.INTEGER)
165                 return "SERIAL PRIMARY KEY";
166             return "BIGSERIAL PRIMARY KEY";
167         }
168         string def = "";
169         int len = 0;
170         if (cast(NumberType)type !is null) {
171             len = (cast(NumberType)type).length;
172         }
173         if (cast(StringType)type !is null) {
174             len = (cast(StringType)type).length;
175         }
176         string modifiers = nullablility ~ def ~ pk;
177         string lenmodifiers = "(" ~ to!string(len > 0 ? len : 255) ~ ")" ~ modifiers;
178         switch (sqlType) {
179             case SqlType.BIGINT:
180                 return "BIGINT" ~ modifiers;
181             case SqlType.BIT:
182             case SqlType.BOOLEAN:
183                 return "BOOLEAN" ~ modifiers;
184             case SqlType.INTEGER:
185                 return "INT" ~ modifiers;
186             case SqlType.NUMERIC:
187                 return "INT" ~ modifiers;
188             case SqlType.SMALLINT:
189                 return "SMALLINT" ~ modifiers;
190             case SqlType.TINYINT:
191                 return "SMALLINT" ~ modifiers;
192             case SqlType.FLOAT:
193                 return "FLOAT(24)" ~ modifiers;
194             case SqlType.DOUBLE:
195                 return "FLOAT(53)" ~ modifiers;
196             case SqlType.DECIMAL:
197                 return "REAL" ~ modifiers;
198             case SqlType.DATE:
199                 return "DATE" ~ modifiers;
200             case SqlType.DATETIME:
201                 return "TIMESTAMP" ~ modifiers;
202             case SqlType.TIME:
203                 return "TIME" ~ modifiers;
204             case SqlType.CHAR:
205             case SqlType.CLOB:
206             case SqlType.LONGNVARCHAR:
207             case SqlType.LONGVARBINARY:
208             case SqlType.LONGVARCHAR:
209             case SqlType.NCHAR:
210             case SqlType.NCLOB:
211             case SqlType.VARBINARY:
212             case SqlType.VARCHAR:
213             case SqlType.NVARCHAR:
214                 return "TEXT" ~ modifiers;
215             case SqlType.BLOB:
216                 return "BYTEA";
217             default:
218                 return "TEXT";
219         }
220     }
221     
222     override string getCheckTableExistsSQL(string tableName) {
223         return "select relname from pg_class where relname = " ~ quoteSqlString(tableName) ~ " and relkind='r'";
224     }
225     
226     override string getUniqueIndexItemSQL(string indexName, string[] columnNames) {
227         return "UNIQUE " ~ createFieldListSQL(columnNames);
228     }
229     
230     /// for some of RDBMS it's necessary to pass additional clauses in query to get generated value (e.g. in Postgres - " returing id"
231     override string appendInsertToFetchGeneratedKey(string query, const EntityInfo entity) {
232         return query ~ " RETURNING " ~ quoteIfNeeded(entity.getKeyProperty().columnName);
233     }
234 
235     this() {
236         addKeywords(PGSQL_RESERVED_WORDS);
237     }
238 }
239