Line 0
Link Here
|
|
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 1998, 2009 Oracle. All rights reserved. |
3 |
* This program and the accompanying materials are made available under the |
4 |
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 |
5 |
* which accompanies this distribution. |
6 |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html |
7 |
* and the Eclipse Distribution License is available at |
8 |
* http://www.eclipse.org/org/documents/edl-v10.php. |
9 |
* |
10 |
* Contributors: |
11 |
* Mike Keith |
12 |
* |
13 |
* Patterned after: |
14 |
* org.eclipse.persistence.platform.database.DB2MainframePlatform |
15 |
******************************************************************************/ |
16 |
package org.eclipse.persistence.platform.database; |
17 |
|
18 |
import java.io.*; |
19 |
import java.util.*; |
20 |
|
21 |
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall; |
22 |
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition; |
23 |
import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter; |
24 |
import org.eclipse.persistence.internal.expressions.ParameterExpression; |
25 |
import org.eclipse.persistence.internal.expressions.SQLSelectStatement; |
26 |
import org.eclipse.persistence.internal.helper.BasicTypeHelperImpl; |
27 |
import org.eclipse.persistence.internal.helper.ClassConstants; |
28 |
import org.eclipse.persistence.internal.helper.DatabaseField; |
29 |
import org.eclipse.persistence.internal.helper.DatabaseTable; |
30 |
import org.eclipse.persistence.internal.sessions.AbstractRecord; |
31 |
import org.eclipse.persistence.platform.database.HSQLPlatform; |
32 |
import org.eclipse.persistence.queries.ValueReadQuery; |
33 |
import org.eclipse.persistence.exceptions.ValidationException; |
34 |
import org.eclipse.persistence.expressions.ExpressionOperator; |
35 |
import org.eclipse.persistence.sequencing.Sequence; |
36 |
import org.eclipse.persistence.sequencing.NativeSequence; |
37 |
|
38 |
public class H2Platform extends HSQLPlatform { |
39 |
private static final long serialVersionUID = -2935483687958482934L; |
40 |
|
41 |
public H2Platform() { |
42 |
super(); |
43 |
setPingSQL("SELECT 1"); |
44 |
setSupportsAutoCommit(true); |
45 |
} |
46 |
|
47 |
@Override |
48 |
public final boolean isHSQL() { |
49 |
return false; |
50 |
} |
51 |
|
52 |
/** |
53 |
* Print the pagination SQL using H2 syntax " LIMIT <max> OFFSET <first>". |
54 |
*/ |
55 |
@Override |
56 |
public void printSQLSelectStatement(DatabaseCall call, ExpressionSQLPrinter printer, SQLSelectStatement statement) { |
57 |
int max = 0; |
58 |
if (statement.getQuery() != null) { |
59 |
max = statement.getQuery().getMaxRows(); |
60 |
} |
61 |
if (max <= 0 || !(this.shouldUseRownumFiltering())) { |
62 |
super.printSQLSelectStatement(call, printer, statement); |
63 |
return; |
64 |
} |
65 |
statement.setUseUniqueFieldAliases(true); |
66 |
call.setFields(statement.printSQL(printer)); |
67 |
printer.printString(" LIMIT "); |
68 |
printer.printParameter(DatabaseCall.MAXROW_FIELD); |
69 |
printer.printString(" OFFSET "); |
70 |
printer.printParameter(DatabaseCall.FIRSTRESULT_FIELD); |
71 |
call.setIgnoreFirstRowMaxResultsSettings(true); |
72 |
} |
73 |
|
74 |
|
75 |
/** |
76 |
* INTERNAL: |
77 |
* Use the JDBC maxResults and firstResultIndex setting to compute a value to use when |
78 |
* limiting the results of a query in SQL. These limits tend to be used in two ways. |
79 |
* |
80 |
* 1. MaxRows is the index of the last row to be returned (like JDBC maxResults) |
81 |
* 2. MaxRows is the number of rows to be returned |
82 |
* |
83 |
* H2 uses case #2 and therefore the maxResults has to be altered based on the firstResultIndex. |
84 |
*/ |
85 |
@Override |
86 |
public int computeMaxRowsForSQL(int firstResultIndex, int maxResults){ |
87 |
return maxResults - ((firstResultIndex >= 0) ? firstResultIndex : 0); |
88 |
} |
89 |
|
90 |
@Override |
91 |
@SuppressWarnings("unchecked") |
92 |
protected Hashtable buildFieldTypes() { |
93 |
Hashtable fieldTypeMapping = super.buildFieldTypes(); |
94 |
fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATE", false)); |
95 |
fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("TIME", false)); |
96 |
fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("TIMESTAMP", false)); |
97 |
fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("BLOB", false)); |
98 |
fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("CLOB", false)); |
99 |
fieldTypeMapping.put(Float.class, new FieldTypeDefinition("DOUBLE", false)); |
100 |
fieldTypeMapping.put(Double.class, new FieldTypeDefinition("DOUBLE", false)); |
101 |
fieldTypeMapping.put(Boolean.class, new FieldTypeDefinition("BOOLEAN", false)); |
102 |
return fieldTypeMapping; |
103 |
} |
104 |
|
105 |
@Override |
106 |
public boolean isAlterSequenceObjectSupported() { |
107 |
return true; |
108 |
} |
109 |
|
110 |
@Override |
111 |
public ValueReadQuery buildSelectQueryForSequenceObject(String seqName, Integer size) { |
112 |
return new ValueReadQuery(new StringBuilder(20 + seqName.length()).append("CALL NEXT VALUE FOR ").append(seqName).toString()); |
113 |
} |
114 |
|
115 |
@Override |
116 |
protected Sequence createPlatformDefaultSequence() { |
117 |
return new NativeSequence(); |
118 |
} |
119 |
|
120 |
@Override |
121 |
public boolean supportsIdentity() { |
122 |
return true; |
123 |
} |
124 |
|
125 |
@Override |
126 |
public boolean supportsSequenceObjects() { |
127 |
return true; |
128 |
} |
129 |
|
130 |
@Override |
131 |
public ValueReadQuery buildSelectQueryForIdentity() { |
132 |
return new ValueReadQuery("CALL IDENTITY()"); |
133 |
} |
134 |
|
135 |
@Override |
136 |
public void printFieldIdentityClause(Writer writer) throws ValidationException { |
137 |
try { |
138 |
writer.append(" IDENTITY"); |
139 |
} catch (IOException e) { |
140 |
throw ValidationException.logIOError(e); |
141 |
} |
142 |
} |
143 |
|
144 |
@Override |
145 |
public boolean supportsForeignKeyConstraints() { |
146 |
return true; |
147 |
} |
148 |
|
149 |
@Override |
150 |
public boolean supportsLocalTempTables() { |
151 |
return true; |
152 |
} |
153 |
|
154 |
@Override |
155 |
public boolean supportsGlobalTempTables() { |
156 |
return true; |
157 |
} |
158 |
|
159 |
@Override |
160 |
protected String getCreateTempTableSqlPrefix() { |
161 |
return "CREATE TEMPORARY TABLE IF NOT EXISTS "; |
162 |
} |
163 |
|
164 |
/** |
165 |
* H2 does not allow using () in the update if only one field. |
166 |
*/ |
167 |
@Override |
168 |
public void writeUpdateOriginalFromTempTableSql(Writer writer, DatabaseTable table, |
169 |
Collection pkFields, |
170 |
Collection assignedFields) throws IOException |
171 |
{ |
172 |
writer.write("UPDATE "); |
173 |
String tableName = table.getQualifiedNameDelimited(this); |
174 |
writer.write(tableName); |
175 |
writer.write(" SET "); |
176 |
if (assignedFields.size() > 1) { |
177 |
writer.write("("); |
178 |
} |
179 |
writeFieldsList(writer, assignedFields, this); |
180 |
if (assignedFields.size() > 1) { |
181 |
writer.write(")"); |
182 |
} |
183 |
writer.write(" = (SELECT "); |
184 |
writeFieldsList(writer, assignedFields, this); |
185 |
writer.write(" FROM "); |
186 |
String tempTableName = getTempTableForTable(table).getQualifiedNameDelimited(this); |
187 |
writer.write(tempTableName); |
188 |
writeAutoJoinWhereClause(writer, null, tableName, pkFields, this); |
189 |
writer.write(") WHERE EXISTS(SELECT "); |
190 |
writer.write(((DatabaseField)pkFields.iterator().next()).getNameDelimited(this)); |
191 |
writer.write(" FROM "); |
192 |
writer.write(tempTableName); |
193 |
writeAutoJoinWhereClause(writer, null, tableName, pkFields, this); |
194 |
writer.write(")"); |
195 |
} |
196 |
|
197 |
@Override |
198 |
public boolean supportsNativeSequenceNumbers() { |
199 |
return true; |
200 |
} |
201 |
|
202 |
@Override |
203 |
public boolean supportsStoredFunctions() { |
204 |
return true; |
205 |
} |
206 |
|
207 |
@Override |
208 |
public ValueReadQuery getTimestampQuery() { |
209 |
return new ValueReadQuery("SELECT CURRENT_TIMESTAMP()"); |
210 |
} |
211 |
|
212 |
@Override |
213 |
protected void initializePlatformOperators() { |
214 |
super.initializePlatformOperators(); |
215 |
addOperator(ExpressionOperator.simpleMath(ExpressionOperator.Concat, "||")); |
216 |
addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.Ceil, "CEILING")); |
217 |
addOperator(ExpressionOperator.simpleTwoArgumentFunction(ExpressionOperator.Nvl, "IFNULL")); |
218 |
addOperator(toNumberOperator()); |
219 |
addOperator(monthsBetweenOperator()); |
220 |
} |
221 |
|
222 |
/** |
223 |
* INTERNAL: |
224 |
* Use CONVERT function for toNumber. |
225 |
*/ |
226 |
public static ExpressionOperator toNumberOperator() { |
227 |
ExpressionOperator exOperator = new ExpressionOperator(); |
228 |
exOperator.setType(ExpressionOperator.FunctionOperator); |
229 |
exOperator.setSelector(ExpressionOperator.ToNumber); |
230 |
Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2); |
231 |
v.addElement("CONVERT("); |
232 |
v.addElement(",DECIMAL)"); |
233 |
exOperator.printsAs(v); |
234 |
exOperator.bePrefix(); |
235 |
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class); |
236 |
return exOperator; |
237 |
} |
238 |
|
239 |
/** |
240 |
* INTERNAL: |
241 |
* Use MONTH function for MONTH_BETWEEN. |
242 |
*/ |
243 |
public static ExpressionOperator monthsBetweenOperator() { |
244 |
ExpressionOperator exOperator = new ExpressionOperator(); |
245 |
exOperator.setType(ExpressionOperator.FunctionOperator); |
246 |
exOperator.setSelector(ExpressionOperator.MonthsBetween); |
247 |
Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2); |
248 |
v.addElement("(MONTH("); |
249 |
v.addElement(") - MONTH("); |
250 |
v.addElement("))"); |
251 |
exOperator.printsAs(v); |
252 |
exOperator.bePrefix(); |
253 |
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class); |
254 |
return exOperator; |
255 |
} |
256 |
|
257 |
/** |
258 |
* INTERNAL |
259 |
* H2 has some issue with un-typed parameters, this allows a workaround to these issues using literals. |
260 |
*/ |
261 |
public boolean shouldBindLiterals() { |
262 |
return false; |
263 |
} |
264 |
|
265 |
public boolean isH2() { |
266 |
return true; |
267 |
} |
268 |
} |