001: /*
002: * HA-JDBC: High-Availability JDBC
003: * Copyright (c) 2004-2007 Paul Ferraro
004: *
005: * This library is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU Lesser General Public License as published by the
007: * Free Software Foundation; either version 2.1 of the License, or (at your
008: * option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
013: * for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public License
016: * along with this library; if not, write to the Free Software Foundation,
017: * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * Contact: ferraro@users.sourceforge.net
020: */
021: package net.sf.hajdbc.dialect;
022:
023: import java.sql.Connection;
024: import java.sql.DatabaseMetaData;
025: import java.sql.ResultSet;
026: import java.sql.SQLException;
027: import java.sql.Statement;
028: import java.sql.Types;
029: import java.util.Collection;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.regex.Pattern;
033:
034: import net.sf.hajdbc.ColumnProperties;
035: import net.sf.hajdbc.Dialect;
036: import net.sf.hajdbc.ForeignKeyConstraint;
037: import net.sf.hajdbc.QualifiedName;
038: import net.sf.hajdbc.SequenceProperties;
039: import net.sf.hajdbc.TableProperties;
040: import net.sf.hajdbc.UniqueConstraint;
041: import net.sf.hajdbc.cache.ForeignKeyConstraintImpl;
042: import net.sf.hajdbc.cache.UniqueConstraintImpl;
043:
044: import org.easymock.EasyMock;
045: import org.testng.annotations.AfterMethod;
046: import org.testng.annotations.DataProvider;
047: import org.testng.annotations.Test;
048:
049: /**
050: * @author Paul Ferraro
051: *
052: */
053: @SuppressWarnings("nls")
054: public class TestStandardDialect implements Dialect {
055: protected TableProperties tableProperties = EasyMock
056: .createStrictMock(TableProperties.class);
057: protected Connection connection = EasyMock
058: .createStrictMock(Connection.class);
059: protected ColumnProperties columnProperties = EasyMock
060: .createStrictMock(ColumnProperties.class);
061: protected SequenceProperties sequenceProperties = EasyMock
062: .createStrictMock(SequenceProperties.class);
063: protected DatabaseMetaData metaData = EasyMock
064: .createStrictMock(DatabaseMetaData.class);
065: protected Statement statement = EasyMock
066: .createStrictMock(Statement.class);
067: protected ResultSet resultSet = EasyMock
068: .createStrictMock(ResultSet.class);
069:
070: protected Dialect dialect = this .createDialect();
071:
072: protected Dialect createDialect() {
073: return new StandardDialect();
074: }
075:
076: void replay() {
077: EasyMock.replay(this .getMocks());
078: }
079:
080: void verify() {
081: EasyMock.verify(this .getMocks());
082: }
083:
084: @AfterMethod
085: void reset() {
086: EasyMock.reset(this .getMocks());
087: }
088:
089: private Object[] getMocks() {
090: return new Object[] { this .tableProperties, this .connection,
091: this .columnProperties, this .metaData, this .statement,
092: this .resultSet, this .sequenceProperties };
093: }
094:
095: @DataProvider(name="table")
096: Object[][] tableProvider() {
097: return new Object[][] { new Object[] { this .tableProperties } };
098: }
099:
100: @DataProvider(name="foreign-key")
101: Object[][] foreignKeyProvider() {
102: ForeignKeyConstraint foreignKey = new ForeignKeyConstraintImpl(
103: "name", "table");
104: foreignKey.getColumnList().add("column1");
105: foreignKey.getColumnList().add("column2");
106: foreignKey.setForeignTable("foreign_table");
107: foreignKey.getForeignColumnList().add("foreign_column1");
108: foreignKey.getForeignColumnList().add("foreign_column2");
109: foreignKey
110: .setDeferrability(DatabaseMetaData.importedKeyInitiallyDeferred);
111: foreignKey.setDeleteRule(DatabaseMetaData.importedKeyCascade);
112: foreignKey.setUpdateRule(DatabaseMetaData.importedKeyRestrict);
113:
114: return new Object[][] { new Object[] { foreignKey } };
115: }
116:
117: @DataProvider(name="unique-constraint")
118: Object[][] uniqueConstraintProvider() {
119: UniqueConstraint uniqueKey = new UniqueConstraintImpl("name",
120: "table");
121: uniqueKey.getColumnList().add("column1");
122: uniqueKey.getColumnList().add("column2");
123:
124: return new Object[][] { new Object[] { uniqueKey } };
125: }
126:
127: @DataProvider(name="column")
128: Object[][] columnProvider() {
129: return new Object[][] { new Object[] { this .columnProperties } };
130: }
131:
132: @DataProvider(name="null")
133: Object[][] nullProvider() {
134: return new Object[][] { new Object[] { null } };
135: }
136:
137: @DataProvider(name="sequence")
138: Object[][] sequenceProvider() {
139: return new Object[][] { new Object[] { this .sequenceProperties } };
140: }
141:
142: @DataProvider(name="sequence-long")
143: Object[][] alterSequenceProvider() {
144: return new Object[][] { new Object[] { this .sequenceProperties,
145: 1000L } };
146: }
147:
148: @Override
149: @Test(dataProvider="sequence-long")
150: public String getAlterSequenceSQL(SequenceProperties sequence,
151: long value) throws SQLException {
152: EasyMock.expect(sequence.getName()).andReturn("sequence");
153:
154: this .replay();
155:
156: String sql = this .dialect.getAlterSequenceSQL(sequence, value);
157:
158: this .verify();
159:
160: assert sql.equals("ALTER SEQUENCE sequence RESTART WITH 1000") : sql;
161:
162: return sql;
163: }
164:
165: @Override
166: @Test(dataProvider="column")
167: public int getColumnType(ColumnProperties properties)
168: throws SQLException {
169: EasyMock.expect(properties.getType()).andReturn(Types.INTEGER);
170:
171: this .replay();
172:
173: int type = this .dialect.getColumnType(properties);
174:
175: this .verify();
176:
177: assert type == Types.INTEGER : type;
178:
179: return type;
180: }
181:
182: @Override
183: @Test(dataProvider="foreign-key")
184: public String getCreateForeignKeyConstraintSQL(
185: ForeignKeyConstraint constraint) throws SQLException {
186: this .replay();
187:
188: String sql = this .dialect
189: .getCreateForeignKeyConstraintSQL(constraint);
190:
191: this .verify();
192:
193: assert sql
194: .equals("ALTER TABLE table ADD CONSTRAINT name FOREIGN KEY (column1, column2) REFERENCES foreign_table (foreign_column1, foreign_column2) ON DELETE CASCADE ON UPDATE RESTRICT DEFERRABLE INITIALLY DEFERRED") : sql;
195:
196: return sql;
197: }
198:
199: @Override
200: @Test(dataProvider="unique-constraint")
201: public String getCreateUniqueConstraintSQL(
202: UniqueConstraint constraint) throws SQLException {
203: this .replay();
204:
205: String sql = this .dialect
206: .getCreateUniqueConstraintSQL(constraint);
207:
208: this .verify();
209:
210: assert sql
211: .equals("ALTER TABLE table ADD CONSTRAINT name UNIQUE (column1, column2)") : sql;
212:
213: return sql;
214: }
215:
216: @Override
217: @Test(dataProvider="foreign-key")
218: public String getDropForeignKeyConstraintSQL(
219: ForeignKeyConstraint constraint) throws SQLException {
220: this .replay();
221:
222: String sql = this .dialect
223: .getDropForeignKeyConstraintSQL(constraint);
224:
225: this .verify();
226:
227: assert sql.equals("ALTER TABLE table DROP CONSTRAINT name") : sql;
228:
229: return sql;
230: }
231:
232: @Override
233: @Test(dataProvider="unique-constraint")
234: public String getDropUniqueConstraintSQL(UniqueConstraint constraint)
235: throws SQLException {
236: this .replay();
237:
238: String sql = this .dialect
239: .getDropUniqueConstraintSQL(constraint);
240:
241: this .verify();
242:
243: assert sql.equals("ALTER TABLE table DROP CONSTRAINT name") : sql;
244:
245: return sql;
246: }
247:
248: /*
249: @Override
250: @Test(dataProvider = "table")
251: public String getLockTableSQL(TableProperties properties) throws SQLException
252: {
253: UniqueConstraint primaryKey = new UniqueConstraintImpl("name", "table");
254: primaryKey.getColumnList().add("column1");
255: primaryKey.getColumnList().add("column2");
256:
257: EasyMock.expect(properties.getName()).andReturn("table");
258: EasyMock.expect(properties.getPrimaryKey()).andReturn(primaryKey);
259:
260: this.replay();
261:
262: String sql = this.dialect.getLockTableSQL(properties);
263:
264: this.verify();
265:
266: assert sql.equals("UPDATE table SET column1 = column1, column2 = column2") : sql;
267:
268: this.reset();
269:
270: EasyMock.expect(properties.getName()).andReturn("table");
271: EasyMock.expect(properties.getPrimaryKey()).andReturn(null);
272: EasyMock.expect(properties.getColumns()).andReturn(primaryKey.getColumnList());
273:
274: this.replay();
275:
276: sql = this.dialect.getLockTableSQL(properties);
277:
278: this.verify();
279:
280: assert sql.equals("UPDATE table SET column1 = column1, column2 = column2") : sql;
281:
282: return sql;
283: }
284: */
285: @Override
286: @Test(dataProvider="sequence")
287: public String getNextSequenceValueSQL(SequenceProperties sequence)
288: throws SQLException {
289: EasyMock.expect(sequence.getName()).andReturn("sequence");
290:
291: this .replay();
292:
293: String sql = this .dialect.getNextSequenceValueSQL(sequence);
294:
295: this .verify();
296:
297: assert sql.equals("SELECT NEXT VALUE FOR sequence") : sql;
298:
299: return sql;
300: }
301:
302: @Override
303: @Test(dataProvider="meta-data")
304: public Collection<QualifiedName> getSequences(
305: DatabaseMetaData metaData) throws SQLException {
306: EasyMock.expect(
307: this .metaData.getTables(EasyMock.eq(""), EasyMock
308: .eq((String) null), EasyMock.eq("%"), EasyMock
309: .aryEq(new String[] { "SEQUENCE" })))
310: .andReturn(this .resultSet);
311: EasyMock.expect(this .resultSet.next()).andReturn(true);
312: EasyMock.expect(this .resultSet.getString("TABLE_SCHEM"))
313: .andReturn("schema1");
314: EasyMock.expect(this .resultSet.getString("TABLE_NAME"))
315: .andReturn("sequence1");
316: EasyMock.expect(this .resultSet.next()).andReturn(true);
317: EasyMock.expect(this .resultSet.getString("TABLE_SCHEM"))
318: .andReturn("schema2");
319: EasyMock.expect(this .resultSet.getString("TABLE_NAME"))
320: .andReturn("sequence2");
321: EasyMock.expect(this .resultSet.next()).andReturn(false);
322:
323: this .resultSet.close();
324:
325: this .replay();
326:
327: Collection<QualifiedName> sequences = this .dialect
328: .getSequences(metaData);
329:
330: this .verify();
331:
332: assert sequences.size() == 2 : sequences;
333:
334: Iterator<QualifiedName> iterator = sequences.iterator();
335: QualifiedName sequence = iterator.next();
336: String schema = sequence.getSchema();
337: String name = sequence.getName();
338:
339: assert schema.equals("schema1") : schema;
340: assert name.equals("sequence1") : name;
341:
342: sequence = iterator.next();
343: schema = sequence.getSchema();
344: name = sequence.getName();
345:
346: assert schema.equals("schema2") : schema;
347: assert name.equals("sequence2") : name;
348:
349: return sequences;
350: }
351:
352: @Override
353: @Test
354: public String getSimpleSQL() throws SQLException {
355: this .replay();
356:
357: String sql = this .dialect.getSimpleSQL();
358:
359: this .verify();
360:
361: assert sql.equals("SELECT CURRENT_TIMESTAMP") : sql;
362:
363: return sql;
364: }
365:
366: @Override
367: @Test(dataProvider="table")
368: public String getTruncateTableSQL(TableProperties properties)
369: throws SQLException {
370: EasyMock.expect(properties.getName()).andReturn("table");
371:
372: this .replay();
373:
374: String sql = this .dialect.getTruncateTableSQL(properties);
375:
376: this .verify();
377:
378: assert sql.equals("DELETE FROM table");
379:
380: return sql;
381: }
382:
383: @DataProvider(name="select-for-update-sql")
384: Object[][] selectForUpdateProvider() {
385: return new Object[][] {
386: new Object[] { "SELECT * FROM success FOR UPDATE" },
387: new Object[] { "SELECT * FROM failure" }, };
388: }
389:
390: @Override
391: @Test(dataProvider="select-for-update-sql")
392: public boolean isSelectForUpdate(String sql) throws SQLException {
393: this .replay();
394:
395: boolean selectForUpdate = this .dialect.isSelectForUpdate(sql);
396:
397: this .verify();
398:
399: assert selectForUpdate == sql.contains("success");
400:
401: return selectForUpdate;
402: }
403:
404: @DataProvider(name="sequence-sql")
405: Object[][] sequenceSQLProvider() {
406: return new Object[][] {
407: new Object[] { "SELECT NEXT VALUE FOR success" },
408: new Object[] { "SELECT NEXT VALUE FOR success, * FROM table" },
409: new Object[] { "INSERT INTO table VALUES (NEXT VALUE FOR success, 0)" },
410: new Object[] { "UPDATE table SET id = NEXT VALUE FOR success" },
411: new Object[] { "SELECT * FROM table" }, };
412: }
413:
414: @Override
415: @Test(dataProvider="sequence-sql")
416: public String parseSequence(String sql) throws SQLException {
417: this .replay();
418:
419: String sequence = this .dialect.parseSequence(sql);
420:
421: this .verify();
422:
423: if (sql.contains("success")) {
424: assert (sequence != null);
425: assert sequence.equals("success") : sequence;
426: } else {
427: assert (sequence == null) : sequence;
428: }
429:
430: return sequence;
431: }
432:
433: @Override
434: @Test(dataProvider="meta-data")
435: public List<String> getDefaultSchemas(DatabaseMetaData metaData)
436: throws SQLException {
437: String user = "user";
438:
439: EasyMock.expect(this .metaData.getUserName()).andReturn(user);
440:
441: this .replay();
442:
443: List<String> schemaList = this .dialect
444: .getDefaultSchemas(metaData);
445:
446: this .verify();
447:
448: assert schemaList.size() == 1 : schemaList.size();
449:
450: String schema = schemaList.get(0);
451:
452: assert schema.equals(user) : schema;
453:
454: return schemaList;
455: }
456:
457: @Override
458: @Test(dataProvider="column")
459: public boolean isIdentity(ColumnProperties properties)
460: throws SQLException {
461: EasyMock.expect(properties.getRemarks()).andReturn(
462: "GENERATED BY DEFAULT AS IDENTITY");
463:
464: this .replay();
465:
466: boolean identity = this .dialect.isIdentity(properties);
467:
468: this .verify();
469:
470: assert identity;
471:
472: this .reset();
473:
474: EasyMock.expect(this .columnProperties.getRemarks()).andReturn(
475: null);
476:
477: this .replay();
478:
479: identity = this .dialect.isIdentity(properties);
480:
481: this .verify();
482:
483: assert !identity;
484:
485: return identity;
486: }
487:
488: @DataProvider(name="insert-table-sql")
489: Object[][] insertTableProvider() {
490: return new Object[][] {
491: new Object[] { "INSERT INTO success (column1, column2) VALUES (1, 2)" },
492: new Object[] { "INSERT INTO success VALUES (1, 2)" },
493: new Object[] { "INSERT success (column1, column2) VALUES (1, 2)" },
494: new Object[] { "INSERT success VALUES (1, 2)" },
495: new Object[] { "INSERT INTO success (column1, column2) SELECT column1, column2 FROM dummy" },
496: new Object[] { "INSERT INTO success SELECT column1, column2 FROM dummy" },
497: new Object[] { "INSERT success (column1, column2) SELECT column1, column2 FROM dummy" },
498: new Object[] { "INSERT success SELECT column1, column2 FROM dummy" },
499: new Object[] { "SELECT * FROM failure WHERE 0=1" },
500: new Object[] { "UPDATE failure SET column = 0" }, };
501: }
502:
503: @Override
504: @Test(dataProvider="insert-table-sql")
505: public String parseInsertTable(String sql) throws SQLException {
506: this .replay();
507:
508: String table = this .dialect.parseInsertTable(sql);
509:
510: this .verify();
511:
512: if (sql.contains("success")) {
513: assert table != null;
514: assert table.equals("success");
515: } else {
516: assert table == null : table;
517: }
518:
519: return table;
520: }
521:
522: @DataProvider(name="meta-data")
523: Object[][] metaDataProvider() {
524: return new Object[][] { new Object[] { this .metaData } };
525: }
526:
527: @Override
528: @Test(dataProvider="meta-data")
529: public Pattern getIdentifierPattern(DatabaseMetaData metaData)
530: throws SQLException {
531: EasyMock.expect(metaData.getExtraNameCharacters()).andReturn(
532: "-");
533:
534: this .replay();
535:
536: Pattern pattern = this .dialect.getIdentifierPattern(metaData);
537:
538: this .verify();
539:
540: assert pattern.pattern().equals("[\\w\\Q-\\E]+");
541:
542: return pattern;
543: }
544:
545: @DataProvider(name="current-date")
546: Object[][] currentDateProvider() {
547: java.sql.Date date = new java.sql.Date(System
548: .currentTimeMillis());
549:
550: return new Object[][] {
551: new Object[] { "SELECT CURRENT_DATE FROM success", date },
552: new Object[] { "SELECT CCURRENT_DATE FROM failure",
553: date },
554: new Object[] { "SELECT CURRENT_DATES FROM failure",
555: date },
556: new Object[] { "SELECT 1 FROM failure", date }, };
557: }
558:
559: @Override
560: @Test(dataProvider="current-date")
561: public String evaluateCurrentDate(String sql, java.sql.Date date)
562: throws SQLException {
563: String expected = sql.contains("success") ? "SELECT DATE '"
564: + date.toString() + "' FROM success" : sql;
565:
566: String evaluated = this .dialect.evaluateCurrentDate(sql, date);
567:
568: assert evaluated.equals(expected) : evaluated;
569:
570: return evaluated;
571: }
572:
573: @DataProvider(name="current-time")
574: Object[][] currentTimeProvider() {
575: java.sql.Time date = new java.sql.Time(System
576: .currentTimeMillis());
577:
578: return new Object[][] {
579: new Object[] { "SELECT CURRENT_TIME FROM success", date },
580: new Object[] { "SELECT CURRENT_TIME(2) FROM success",
581: date },
582: new Object[] {
583: "SELECT CURRENT_TIME ( 2 ) FROM success", date },
584: new Object[] { "SELECT LOCALTIME FROM success", date },
585: new Object[] { "SELECT LOCALTIME(2) FROM success", date },
586: new Object[] { "SELECT LOCALTIME ( 2 ) FROM success",
587: date },
588: new Object[] { "SELECT CCURRENT_TIME FROM failure",
589: date },
590: new Object[] { "SELECT LLOCALTIME FROM failure", date },
591: new Object[] { "SELECT CURRENT_TIMESTAMP FROM failure",
592: date },
593: new Object[] { "SELECT LOCALTIMESTAMP FROM failure",
594: date },
595: new Object[] { "SELECT 1 FROM failure", date }, };
596: }
597:
598: @Override
599: @Test(dataProvider="current-time")
600: public String evaluateCurrentTime(String sql, java.sql.Time date)
601: throws SQLException {
602: String expected = sql.contains("success") ? "SELECT TIME '"
603: + date.toString() + "' FROM success" : sql;
604:
605: String evaluated = this .dialect.evaluateCurrentTime(sql, date);
606:
607: assert evaluated.equals(expected) : evaluated;
608:
609: return evaluated;
610: }
611:
612: @DataProvider(name="current-timestamp")
613: Object[][] currentTimestampProvider() {
614: java.sql.Timestamp date = new java.sql.Timestamp(System
615: .currentTimeMillis());
616:
617: return new Object[][] {
618: new Object[] { "SELECT CURRENT_TIMESTAMP FROM success",
619: date },
620: new Object[] {
621: "SELECT CURRENT_TIMESTAMP(2) FROM success",
622: date },
623: new Object[] {
624: "SELECT CURRENT_TIMESTAMP ( 2 ) FROM success",
625: date },
626: new Object[] { "SELECT LOCALTIMESTAMP FROM success",
627: date },
628: new Object[] { "SELECT LOCALTIMESTAMP(2) FROM success",
629: date },
630: new Object[] {
631: "SELECT LOCALTIMESTAMP ( 2 ) FROM success",
632: date },
633: new Object[] {
634: "SELECT CURRENT_TIMESTAMPS FROM failure", date },
635: new Object[] {
636: "SELECT CCURRENT_TIMESTAMP FROM failure", date },
637: new Object[] { "SELECT LOCALTIMESTAMPS FROM failure",
638: date },
639: new Object[] { "SELECT LLOCALTIMESTAMP FROM failure",
640: date },
641: new Object[] { "SELECT 1 FROM failure", date }, };
642: }
643:
644: @Override
645: @Test(dataProvider="current-timestamp")
646: public String evaluateCurrentTimestamp(String sql,
647: java.sql.Timestamp date) throws SQLException {
648: String expected = sql.contains("success") ? "SELECT TIMESTAMP '"
649: + date.toString() + "' FROM success"
650: : sql;
651:
652: String evaluated = this .dialect.evaluateCurrentTimestamp(sql,
653: date);
654:
655: assert evaluated.equals(expected) : evaluated;
656:
657: return evaluated;
658: }
659:
660: @DataProvider(name="random")
661: Object[][] randomProvider() {
662: return new Object[][] {
663: new Object[] { "SELECT RAND() FROM success" },
664: new Object[] { "SELECT RAND ( ) FROM success" },
665: new Object[] { "SELECT RAND FROM failure" },
666: new Object[] { "SELECT OPERAND() FROM failure" },
667: new Object[] { "SELECT 1 FROM failure" }, };
668: }
669:
670: @Override
671: @Test(dataProvider="random")
672: public String evaluateRand(String sql) throws SQLException {
673: String evaluated = this .dialect.evaluateRand(sql);
674:
675: if (sql.contains("success")) {
676: assert Pattern.matches(
677: "SELECT 0\\.\\d+(E-\\d+)? FROM success", evaluated) : evaluated;
678: } else {
679: assert evaluated.equals(sql) : evaluated;
680: }
681:
682: return evaluated;
683: }
684:
685: @DataProvider(name="table-column-long")
686: Object[][] tableColumnLongProvider() {
687: return new Object[][] { new Object[] { this .tableProperties,
688: this .columnProperties, 1000L } };
689: }
690:
691: @Override
692: @Test(dataProvider="table-column-long")
693: public String getAlterIdentityColumnSQL(TableProperties table,
694: ColumnProperties column, long value) throws SQLException {
695: EasyMock.expect(table.getName()).andReturn("table");
696: EasyMock.expect(column.getName()).andReturn("column");
697:
698: this .replay();
699:
700: String sql = this .dialect.getAlterIdentityColumnSQL(table,
701: column, value);
702:
703: this .verify();
704:
705: assert sql
706: .equals("ALTER TABLE table ALTER COLUMN column RESTART WITH 1000") : sql;
707:
708: return sql;
709: }
710: }
|