package net.sourceforge.mayfly.acceptance;
import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import net.sourceforge.mayfly.Database;
import net.sourceforge.mayfly.MayflySqlException;
import net.sourceforge.mayfly.dump.SqlDumper;
import java.sql.Connection;
import java.sql.SQLException;
public class MayflyDialect extends Dialect {
private Database database;
@Override
public Connection openConnection() throws Exception {
database = new Database();
return openAdditionalConnection();
}
@Override
public Connection openAdditionalConnection() throws SQLException {
return database.openConnection();
}
@Override
public void assertTableCount(int expected) {
Assert.assertEquals(expected, database.tables().size());
}
@Override
public void checkDump(String expected) {
String dump = new SqlDumper().dump(database.dataStore());
Assert.assertEquals(expected, dump);
}
@Override
public void shutdown(Connection connection) {
}
@Override
public void assertMessage(String expectedMessage, SQLException exception) {
Assert.assertEquals(expectedMessage, exception.getMessage());
}
@Override
public void assertMessage(String expectedMessage, SQLException exception,
int expectedStartLine, int expectedStartColumn, int expectedEndLine, int expectedEndColumn) {
super.assertMessage(expectedMessage, exception, expectedStartLine,
expectedStartColumn, expectedEndLine, expectedEndColumn);
MayflySqlException mayflyException = (MayflySqlException) exception;
assertLocation(expectedStartLine,
mayflyException.startLineNumber(), mayflyException);
assertLocation(expectedStartColumn,
mayflyException.startColumn(), mayflyException);
assertLocation(expectedEndLine,
mayflyException.endLineNumber(), mayflyException);
assertLocation(expectedEndColumn,
mayflyException.endColumn(), mayflyException);
}
private void assertLocation(int expected, int actual,
Exception exception) throws AssertionFailedError {
/* The point is we want to see exception so we can proceed
directly to debugging why it doesn't know about the
location. The cause seems like an expedient (if
perhaps klugy) way to get that. */
if (expected != actual) {
throw (AssertionFailedError)new AssertionFailedError(
"location wrong: expected " + expected +
" but got " + actual)
.initCause(exception);
}
}
@Override
public boolean expectMayflyBehavior() {
return true;
}
@Override
public boolean isReservedWord(String word) {
// Although some databases don't reserve IF, all that have IF EXISTS
// in their DROP TABLE do. The parsing of DROP TABLE seems pretty
// problematic if IF is not reserved, and furthermore it is the
// kind of word that people probably aren't in the habit of trying
// to use in identifiers, just because it is reserved in so many
// other languages.
return word.equalsIgnoreCase("if")
/**
* Don't see how I can do the MySQL index syntax without INDEX keyword.
*/
|| word.equalsIgnoreCase("index")
;
}
@Override
public boolean haveEngine() {
return true;
}
@Override
public boolean willReadUncommitted() {
/** It isn't clear that there is any way to just
dip our toes into this water. It seems like
we need to maintain a log of changes to the database
in the various connections, and then be able
to apply those logs at commit time.
{@link TransactionTest#testTwoWriters()}
*/
return !wishThisWereTrue();
}
@Override
public boolean haveTransactions() {
/**
* {@link net.sourceforge.mayfly.MayflyConnection#rollback()}
* is not implemented.
*/
return wishThisWereTrue();
}
/**
* @internal
* Stuff to work on.
*/
@Override
public boolean wishThisWereTrue() {
return false;
}
@Override
public boolean haveSql2003AutoIncrement() {
return true;
}
@Override
public boolean haveAutoUnderbarIncrement() {
return true;
}
@Override
public boolean haveIdentity() {
return true;
}
@Override
public boolean haveSequencySerial() {
return true;
}
@Override
public String autoIncrementType() {
return "integer auto_increment";
}
@Override
public boolean haveOnUpdateValue() {
return true;
}
@Override
public boolean trailingSpacesConsultedInComparisons() {
// I guess we should go with the standard instead of hypersonic...
// I don't know, are there any strong arguments one way or the other?
return !wishThisWereTrue();
}
@Override
public boolean onUpdateSetNullAndCascadeMissing() {
return !wishThisWereTrue();
}
@Override
public boolean foreignKeyJustNeedsIndex() {
/* Here we adopt the most permissive rule of our
tested databases. That seems expedient in
terms of porting applications which were written
for MySQL, but not so good in terms of wanting
to catch problems in the SQL with Mayfly,
rather than just having them show up with
the production database (say, Postgres).
We might end up reversing this decision,
or mking it something to set in Options.
*/
return true;
}
/**
Although I have my doubts about whether it is really
desirable to be loose with types like this, at least
for now, compatibility with other databases is
winning out.
*/
@Override
public boolean allowDateInTimestampColumn() {
return true;
}
/**
Although I have my doubts about whether it is really
desirable to have someone give us a date&time, and throw
out the time part of it, at least
for now, compatibility with other databases is
winning out.
*/
@Override
public boolean allowTimestampInDateColumn() {
return true;
}
@Override
public boolean allowOrderByOnDelete() {
/* I don't know how I'd implement ORDER BY on DELETE.
It also doesn't seem very elegant.
Maybe for now just tell people to work around it? */
return false;
}
@Override
public boolean canSumStrings(boolean rowsPresent) {
if (rowsPresent) {
return super.canSumStrings(rowsPresent);
}
else {
/* Interesting. We don't carry the type forward to where we
actually compute the sum. But maybe it would work fine
to have a checking phase ahead of time. */
return true;
}
}
@Override
public boolean createTableCanContainIndex() {
// MySQL compatibility.
return true;
}
@Override
public boolean canIndexPartOfColumn() {
// MySQL compatibility.
return true;
}
@Override
public boolean haveAddColumnAfter() {
return true;
}
@Override
public boolean haveInsertSetSyntax() {
/* Only supported by MySQL as far as I know. But it really
is a better syntax, seems like... */
return true;
}
@Override
public Class typeOfTinyint() {
return Long.class;
}
@Override
public Class typeOfSmallint() {
return Long.class;
}
@Override
public Class typeOfInteger() {
/* Is it important to be compatible with other databases (which
have Integer.class here)? There's also a potential speed/space
issue, but I'm guessing we're a while before the rest of Mayfly
is efficient enough for that to matter.
*/
return Long.class;
}
@Override
public String productName() {
return "Mayfly";
}
@Override
public boolean callJavaMethodAsStoredProcedure() {
return wishThisWereTrue();
}
@Override
public boolean haveConcatBuiltIn() {
// No real downside
return true;
}
@Override
public boolean haveConcatBuiltInWithOneArgument() {
/* Might make it easier to generate SQL automatically?
Not sure whether this is a real use case or an imaginary one. */
return true;
}
@Override
public boolean groupByExpressionSimpleComparator() {
/* Not sure whether this is worth worrying about or not, but maybe
in a different design catching the column which can't be evaluated
would just happen? Based on the H2 error message, it seems so. */
return true;
}
}