Fix a few MariaDB integration tests

This commit is contained in:
Simon Binder 2023-08-02 18:27:52 +02:00
parent 85368912f8
commit 6bedc5f88c
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
6 changed files with 73 additions and 77 deletions

View File

@ -187,6 +187,7 @@ jobs:
dart test
- name: MariaDB integration tests
working-directory: extras/drift_mariadb
continue-on-error: true
run: |
dart pub upgrade
dart test

View File

@ -566,10 +566,21 @@ abstract class DatabaseConnectionUser {
/// Used by generated code to expand array variables.
String $expandVar(int start, int amount) {
final buffer = StringBuffer();
final mark = executor.dialect == SqlDialect.postgres ? '@' : '?';
final variableSymbol = switch (executor.dialect) {
SqlDialect.postgres => r'$',
_ => '?',
};
final supportsIndexedParameters =
executor.dialect.supportsIndexedParameters;
for (var x = 0; x < amount; x++) {
buffer.write('$mark${start + x}');
if (supportsIndexedParameters) {
buffer.write('$variableSymbol${start + x}');
} else {
buffer.write(variableSymbol);
}
if (x != amount - 1) {
buffer.write(', ');
}

View File

@ -480,10 +480,35 @@ class _CastInSqlExpression<D1 extends Object, D2 extends Object>
@override
void writeInto(GenerationContext context) {
final type = DriftSqlType.forType<D2>();
if (type == DriftSqlType.any) {
inner.writeInto(context); // No need to cast
}
final String typeName;
if (context.dialect == SqlDialect.mariadb) {
// MariaDB has a weird cast syntax that uses different type names than the
// ones used in a create table statement.
// ignore: unnecessary_cast
typeName = switch (type as DriftSqlType<Object>) {
DriftSqlType.int ||
DriftSqlType.bigInt ||
DriftSqlType.bool =>
'INTEGER',
DriftSqlType.string => 'CHAR',
DriftSqlType.double => 'DOUBLE',
DriftSqlType.blob => 'BINARY',
DriftSqlType.dateTime => 'DATETIME',
DriftSqlType.any => '',
};
} else {
typeName = type.sqlTypeName(context);
}
context.buffer.write('CAST(');
inner.writeInto(context);
context.buffer.write(' AS ${type.sqlTypeName(context)})');
context.buffer.write(' AS $typeName)');
}
}

View File

@ -151,38 +151,15 @@ class Migrator {
/// [other alter]: https://www.sqlite.org/lang_altertable.html#otheralter
/// [drift docs]: https://drift.simonbinder.eu/docs/advanced-features/migrations/#complex-migrations
Future<void> alterTable(TableMigration migration) async {
final dialect = _db.executor.dialect;
bool foreignKeysEnabled;
if (dialect == SqlDialect.sqlite) {
foreignKeysEnabled =
(await _db.customSelect('PRAGMA foreign_keys').getSingle())
.read<bool>('foreign_keys');
} else if (dialect == SqlDialect.mariadb) {
foreignKeysEnabled = (await _db
.customSelect(
'SELECT @@SESSION.foreign_key_checks as foreign_keys')
.getSingle())
.read<bool>('foreign_keys');
} else {
foreignKeysEnabled =
(await _db.customSelect('PRAGMA foreign_keys').getSingle())
.read<bool>('foreign_keys');
}
final legacyAlterTable = dialect == SqlDialect.mariadb
? null
: (await _db.customSelect('PRAGMA legacy_alter_table').getSingle())
final foreignKeysEnabled =
(await _db.customSelect('PRAGMA foreign_keys').getSingle())
.read<bool>('foreign_keys');
final legacyAlterTable =
(await _db.customSelect('PRAGMA legacy_alter_table').getSingle())
.read<bool>('legacy_alter_table');
if (foreignKeysEnabled) {
if (dialect == SqlDialect.sqlite) {
await _db.customStatement('PRAGMA foreign_keys = OFF;');
} else if (dialect == SqlDialect.mariadb) {
await _db.customStatement('SET FOREIGN_KEY_CHECKS = OFF;');
} else {
await _db.customStatement('PRAGMA foreign_keys = OFF;');
}
await _db.customStatement('PRAGMA foreign_keys = OFF;');
}
final table = migration.affectedTable;
@ -251,7 +228,7 @@ class Migrator {
expressionsForSelect.add(expression);
if (!first) context.buffer.write(', ');
context.buffer.write(column.escapedNameFor(dialect));
context.buffer.write(column.escapedNameFor(context.dialect));
first = false;
}
}
@ -274,26 +251,16 @@ class Migrator {
// we've just dropped the original table), we need to enable the legacy
// option which skips the integrity check.
// See also: https://sqlite.org/forum/forumpost/0e2390093fbb8fd6
if (legacyAlterTable == false) {
if (!legacyAlterTable) {
await _issueCustomQuery('pragma legacy_alter_table = 1;');
}
// Step 7: Rename the new table to the old name
if (dialect == SqlDialect.sqlite) {
await _issueCustomQuery(
'ALTER TABLE ${context.identifier(temporaryName)} '
'RENAME TO ${context.identifier(tableName)}');
} else if (dialect == SqlDialect.mariadb) {
await _issueCustomQuery(
'RENAME TABLE ${context.identifier(temporaryName)} '
'TO ${context.identifier(tableName)}');
} else {
await _issueCustomQuery(
'ALTER TABLE ${context.identifier(temporaryName)} '
'RENAME TO ${context.identifier(tableName)}');
}
await _issueCustomQuery(
'ALTER TABLE ${context.identifier(temporaryName)} '
'RENAME TO ${context.identifier(tableName)}');
if (legacyAlterTable == false) {
if (!legacyAlterTable) {
await _issueCustomQuery('pragma legacy_alter_table = 0;');
}
@ -307,13 +274,7 @@ class Migrator {
// Finally, re-enable foreign keys if they were enabled originally.
if (foreignKeysEnabled) {
if (dialect == SqlDialect.sqlite) {
await _db.customStatement('PRAGMA foreign_keys = ON;');
} else if (dialect == SqlDialect.mariadb) {
await _db.customStatement('SET FOREIGN_KEY_CHECKS = ON;');
} else {
await _db.customStatement('PRAGMA foreign_keys = ON;');
}
await _db.customStatement('PRAGMA foreign_keys = ON;');
}
}
@ -510,17 +471,13 @@ class Migrator {
/// databases.
Future<void> renameTable(TableInfo table, String oldName) async {
final context = _createContext();
final dialect = context.dialect;
if (dialect == SqlDialect.sqlite) {
context.buffer.write('ALTER TABLE ${context.identifier(oldName)} '
'RENAME TO ${context.identifier(table.actualTableName)};');
} else if (dialect == SqlDialect.mariadb) {
context.buffer.write('RENAME TABLE ${context.identifier(oldName)} '
'TO ${context.identifier(table.actualTableName)};');
} else {
context.buffer.write('ALTER TABLE ${context.identifier(oldName)} '
'RENAME TO ${context.identifier(table.actualTableName)};');
}
context.buffer.write(switch (context.dialect) {
SqlDialect.mariadb => 'RENAME TABLE ${context.identifier(oldName)} '
'TO ${context.identifier(table.actualTableName)};',
_ => 'ALTER TABLE ${context.identifier(oldName)} '
'RENAME TO ${context.identifier(table.actualTableName)};',
});
return _issueCustomQuery(context.sql);
}

View File

@ -1,4 +1,4 @@
/// MariaDB
/// Experimental Drift integration for MariaDB.
@experimental
library drift.mariadb;

View File

@ -105,15 +105,16 @@ void crudTests(TestExecutor executor) {
final db = Database(executor.createConnection());
// ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member
if (db.executor.dialect == SqlDialect.postgres) {
await db.customStatement(
'INSERT INTO friendships (first_user, second_user) VALUES (@1, @2)',
<int>[1, 2]);
} else {
await db.customStatement(
'INSERT INTO friendships (first_user, second_user) VALUES (?1, ?2)',
<int>[1, 2]);
}
await db.customStatement(
switch (db.executor.dialect) {
SqlDialect.postgres =>
r'INSERT INTO friendships (first_user, second_user) VALUES ($1, $2)',
SqlDialect.mariadb =>
r'INSERT INTO friendships (first_user, second_user) VALUES (?, ?)',
_ =>
r'INSERT INTO friendships (first_user, second_user) VALUES (?1, ?2)',
},
<int>[1, 2]);
expect(await db.friendsOf(1).get(), isNotEmpty);
await executor.clearDatabaseAndClose(db);
@ -127,7 +128,8 @@ void crudTests(TestExecutor executor) {
Future<T?> evaluate<T extends Object>(Expression<T> expr) async {
late final Expression<T> effectiveExpr;
if (database.executor.dialect == SqlDialect.postgres) {
final dialect = database.executor.dialect;
if (dialect == SqlDialect.postgres || dialect == SqlDialect.mariadb) {
// 'SELECT'ing values that don't come from a table return as String
// by default, so we need to explicitly cast it to the expected type
// https://www.postgresql.org/docs/current/typeconv-select.html