1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.melati.poem.dbms;
44
45 import java.util.Enumeration;
46 import java.sql.SQLException;
47 import java.sql.DatabaseMetaData;
48 import java.sql.PreparedStatement;
49 import java.sql.ResultSet;
50
51 import org.melati.poem.SizedAtomPoemType;
52 import org.melati.poem.Table;
53 import org.melati.poem.Column;
54 import org.melati.poem.PoemType;
55 import org.melati.poem.SQLPoemType;
56 import org.melati.poem.IntegerPoemType;
57 import org.melati.poem.BinaryPoemType;
58 import org.melati.poem.BooleanPoemType;
59 import org.melati.poem.StringPoemType;
60 import org.melati.poem.ParsingPoemException;
61 import org.melati.poem.SQLPoemException;
62 import org.melati.poem.TimestampPoemType;
63 import org.melati.poem.util.StringUtils;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public class MySQL extends AnsiStandard {
125
126
127 public static final int indexSize = 30;
128
129 public static final int mysqlTextSize = 65535;
130
131
132 public MySQL() {
133 setDriverClassName("org.gjt.mm.mysql.Driver");
134 }
135
136
137
138
139
140 public String createTableOptionsSql() {
141
142
143
144 return " ENGINE='InnoDB' ";
145 }
146
147
148
149
150
151
152
153
154 public String getSqlDefinition(String sqlTypeName) {
155 if(sqlTypeName.equals("BOOLEAN")) return "bool";
156 return super.getSqlDefinition(sqlTypeName);
157 }
158
159 @Override
160 public String getStringSqlDefinition(int size) throws SQLException {
161 if (size < 0) {
162 return "text";
163 }
164 return super.getStringSqlDefinition(size);
165 }
166
167
168
169
170 @Override
171 public String getBinarySqlDefinition(int size) {
172 return "BLOB";
173 }
174
175 @Override
176 public String getQuotedName(String name) {
177 return unreservedName(name);
178 }
179
180
181
182
183 public static class MySQLStringPoemType extends StringPoemType {
184
185
186
187
188
189
190 public MySQLStringPoemType(boolean nullable, int size) {
191 super(nullable, size);
192 }
193
194
195 protected boolean _canRepresent(SQLPoemType<?> other) {
196 return
197 sqlTypeCode() == other.sqlTypeCode()
198 && other instanceof StringPoemType
199 && (getSize()<0 || getSize()==mysqlTextSize || getSize()>=((StringPoemType)other).getSize());
200 }
201
202
203
204
205 public SizedAtomPoemType<String> withSize(int newSize) {
206 if (newSize==mysqlTextSize)
207 return super.withSize(-1);
208 return super.withSize(newSize);
209 }
210 }
211
212
213
214
215 public static class MySQLBooleanPoemType extends BooleanPoemType {
216
217
218
219
220 public MySQLBooleanPoemType(boolean nullable) {
221 super(nullable);
222 }
223
224 protected Boolean _getRaw(ResultSet rs, int col) throws SQLException {
225 synchronized (rs) {
226 int i = rs.getInt(col);
227 return rs.wasNull() ? null :
228 (i==1 ? Boolean.TRUE : Boolean.FALSE);
229 }
230 }
231
232
233
234
235
236
237
238
239
240
241 protected void _setRaw(PreparedStatement ps, int col, Object bool)
242 throws SQLException {
243 ps.setInt(col, ((Boolean)bool).booleanValue() ? 1 : 0);
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 protected Boolean _rawOfString(String rawString)
262 throws ParsingPoemException {
263 rawString = rawString.trim();
264 switch (rawString.charAt(0)) {
265 case '1': return Boolean.TRUE;
266 case '0': return Boolean.FALSE;
267 default: throw new ParsingPoemException(this, rawString);
268 }
269 }
270 }
271
272
273
274
275 public static class BlobPoemType extends BinaryPoemType {
276
277
278
279
280
281
282 public BlobPoemType(boolean nullable, int size) {
283 super(nullable, size);
284 }
285
286 protected boolean _canRepresent(SQLPoemType<?> other) {
287 return other instanceof BinaryPoemType;
288 }
289
290 @Override
291 public <O>PoemType<O> canRepresent(PoemType<O> other) {
292 return other instanceof BinaryPoemType &&
293 !(!getNullable() && ((BinaryPoemType)other).getNullable()) ?
294 other : null;
295 }
296 }
297
298 @Override
299 public <S,O>PoemType<O> canRepresent(PoemType<S> storage, PoemType<O> other) {
300 if (storage instanceof IntegerPoemType &&
301 other instanceof BooleanPoemType
302 && !(!storage.getNullable() && other.getNullable())
303 ) {
304 return other;
305 } else {
306 return storage.canRepresent(other);
307 }
308 }
309
310 @Override
311 public SQLPoemType<?> defaultPoemTypeOfColumnMetaData(ResultSet md)
312 throws SQLException {
313 boolean nullable = md.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
314 String typeName = md.getString("TYPE_NAME").toLowerCase();
315 if(typeName.equals("blob"))
316 return new BlobPoemType(nullable, md.getInt("COLUMN_SIZE"));
317 else if(typeName.equals("text"))
318 return new MySQLStringPoemType(nullable, md.getInt("COLUMN_SIZE"));
319 else if(typeName.equals("smallint"))
320 return new IntegerPoemType(nullable);
321
322 else if(typeName.equals("datetime"))
323 return new TimestampPoemType(nullable);
324 else if(typeName.equals("set"))
325 return new StringPoemType(nullable, md.getInt("COLUMN_SIZE"));
326
327 else if(typeName.equals("char"))
328 return new StringPoemType(nullable, md.getInt("COLUMN_SIZE"));
329
330 else if(typeName.equals("tinyint"))
331 return new MySQLBooleanPoemType(nullable);
332 else
333 return super.defaultPoemTypeOfColumnMetaData(md);
334 }
335
336
337 @Override
338 public SQLPoemException exceptionForUpdate(
339 Table<?> table, String sql, boolean insert, SQLException e) {
340
341 String m = e.getMessage();
342
343
344
345
346
347
348
349
350 if (m != null &&
351 m.indexOf("1062") >= 0) {
352
353
354
355
356
357
358
359 try {
360
361 int preIndex, postIndex;
362 int preColumn;
363
364 preIndex= m.indexOf('\'');
365 postIndex= m.lastIndexOf('\'');
366 preColumn= m.indexOf("key ");
367
368 String indexValue= m.substring(preIndex+1, postIndex);
369 String indexColumn= m.substring(preColumn+4);
370
371 System.err.println("Duplicated value " + indexValue +
372 " of " + indexColumn + "th unique field.");
373
374 int indexNum= Integer.parseInt(indexColumn);
375 Column<?> column = table.troidColumn();
376
377
378 for(Enumeration<Column<?>> columns = table.columns(); columns.hasMoreElements();) {
379 column = columns.nextElement();
380 if(column.getUnique() && (--indexNum == 0))
381 break;
382 }
383
384 if(indexNum==0)
385 return new DuplicateKeySQLPoemException(column, sql, insert, e);
386 } catch(NumberFormatException f) {
387 throw new RuntimeException(
388 "Number format exception parsing dbms error.");
389 }
390 return new DuplicateKeySQLPoemException(table, sql, insert, e);
391 }
392 return super.exceptionForUpdate(table, sql, insert, e);
393 }
394
395 @Override
396 public String unreservedName(String name) {
397 if(name.equalsIgnoreCase("group")) name = "poem_" + name;
398 if(name.equalsIgnoreCase("precision")) name = "poem_" + name;
399 if(name.equalsIgnoreCase("unique")) name = "poem_" + name;
400 return name;
401 }
402
403 @Override
404 public String melatiName(String name) {
405 if (name == null) return name;
406 if(name.equalsIgnoreCase("poem_group")) name = "group";
407 if(name.equalsIgnoreCase("poem_precision")) name = "precision";
408 if(name.equalsIgnoreCase("poem_unique")) name = "unique";
409 return name;
410 }
411
412
413
414
415
416
417
418 @Override
419 public String getIndexLength(Column<?> column) {
420 PoemType<?> t = column.getType();
421 if (t instanceof StringPoemType &&
422 ((StringPoemType)t).getSize() < 0) return "(" + indexSize + ")";
423 if (t instanceof BinaryPoemType) return "(" + indexSize + ")";
424 return "";
425 }
426
427 @Override
428 public String givesCapabilitySQL(Integer userTroid, String capabilityExpr) {
429 return
430 "SELECT groupMembership.* " +
431 "FROM groupMembership LEFT JOIN groupCapability " +
432 "ON groupMembership." + getQuotedName("group") +
433 " = groupCapability." + getQuotedName("group") + " " +
434 "WHERE " + getQuotedName("user") + " = " + userTroid + " " +
435 "AND groupCapability." + getQuotedName("group") + " IS NOT NULL " +
436 "AND capability = " + capabilityExpr;
437 }
438
439 @Override
440 public String caseInsensitiveRegExpSQL(String term1, String term2) {
441 if (StringUtils.isQuoted(term2)) {
442 term2 = term2.substring(1, term2.length() - 1);
443 }
444 term2 = StringUtils.quoted(StringUtils.quoted(term2, '%'), '\'');
445
446 return term1 + " LIKE " + term2;
447 }
448
449 @Override
450 public String alterColumnNotNullableSQL(String tableName, Column<?> column) {
451 return "ALTER TABLE " + getQuotedName(tableName) +
452 " CHANGE " + getQuotedName(column.getName()) + " " + getQuotedName(column.getName()) +
453 " " +
454 column.getSQLType().sqlDefinition(this);
455 }
456
457
458
459
460 @Override
461 public String alterColumnAddCommentSQL(Column<?> column, String comment) {
462 return null;
463 }
464
465
466
467
468 @Override
469 public String alterTableAddCommentSQL(Table<?> table, String comment) {
470 return null;
471 }
472
473 }