From f3f64e94dd4b5d43b02a714914cf304ff68c108b Mon Sep 17 00:00:00 2001 From: "(AJ) Zin Kyaw" Date: Tue, 2 Jan 2024 23:10:35 +0630 Subject: [PATCH] Add support for mysql driver (#106) --- .github/workflows/test.yaml | 24 +++- melos.yaml | 17 ++- packages/dox-app/pubspec.lock | 8 ++ .../lib/src/drivers/db_driver.dart | 13 +- .../lib/src/drivers/mysql_driver.dart | 136 ++++++++++++++++++ .../lib/src/drivers/postgres_driver.dart | 10 +- .../dox-query-builder/lib/src/insert.dart | 18 ++- packages/dox-query-builder/lib/src/main.dart | 25 +++- packages/dox-query-builder/lib/src/model.dart | 7 +- .../lib/src/query_builder.dart | 14 +- .../dox-query-builder/lib/src/schema.dart | 2 +- .../lib/src/schema/table.dart | 25 ++-- .../lib/src/schema/table.shared_mixin.dart | 2 +- .../lib/src/schema/table.update.dart | 28 +++- .../lib/src/shared_mixin.dart | 2 +- .../dox-query-builder/lib/src/truncate.dart | 8 +- .../dox-query-builder/lib/src/update.dart | 6 +- .../lib/src/utils/helper.dart | 10 +- packages/dox-query-builder/lib/src/where.dart | 4 +- packages/dox-query-builder/pubspec.yaml | 3 +- .../test/belongs_to_test.dart | 3 +- .../dox-query-builder/test/connection.dart | 39 +++-- .../dox-query-builder/test/has_many_test.dart | 3 +- .../dox-query-builder/test/has_one_test.dart | 3 +- .../test/many_to_many_test.dart | 2 +- .../test/model_custom_table_name_test.dart | 5 +- .../dox-query-builder/test/model_test.dart | 5 +- packages/dox-query-builder/test/mysql.dart | 17 +++ packages/dox-query-builder/test/postgres.dart | 20 +++ .../dox-query-builder/test/schema_test.dart | 3 +- .../test/sql_query_builder_test.dart | 13 +- 31 files changed, 389 insertions(+), 86 deletions(-) create mode 100644 packages/dox-query-builder/lib/src/drivers/mysql_driver.dart create mode 100644 packages/dox-query-builder/test/mysql.dart create mode 100644 packages/dox-query-builder/test/postgres.dart diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0353d2c..1a11fb0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,6 +8,24 @@ jobs: runs-on: ubuntu-latest services: + + mariadb: + image: mariadb:latest + ports: + - 3306:3306 + env: + MYSQL_USER: dox + MYSQL_PASSWORD: password + MYSQL_DATABASE: dox-framework + MYSQL_ROOT_PASSWORD: password + options: >- + --health-cmd="healthcheck.sh + --connect + --innodb_initialized" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + postgres: image: postgres:latest env: @@ -42,4 +60,8 @@ jobs: - name: Melos Bootstrap run: melos bs - name: Run Tests - run: melos test + run: melos test --no-select + - name: Run Test (query builder postgres) + run: melos test_query_builder_postgres + - name: Run Test (query builder mysql) + run: DB_USER=dox melos test_query_builder_mysql diff --git a/melos.yaml b/melos.yaml index 55e9b4c..d14cd28 100644 --- a/melos.yaml +++ b/melos.yaml @@ -4,5 +4,20 @@ packages: - packages/** scripts: + test: - exec: dart test --concurrency=1 \ No newline at end of file + exec: dart test --concurrency=1 + packageFilters: + ignore: "dox_query_builder" + + test_query_builder_postgres: + exec: DRIVER=postgres dart test --concurrency=1 + packageFilters: + noSelect: true + scope: "dox_query_builder" + + test_query_builder_mysql: + exec: DRIVER=mysql dart test --concurrency=1 + packageFilters: + noSelect: true + scope: "dox_query_builder" \ No newline at end of file diff --git a/packages/dox-app/pubspec.lock b/packages/dox-app/pubspec.lock index eb94b30..67231bf 100644 --- a/packages/dox-app/pubspec.lock +++ b/packages/dox-app/pubspec.lock @@ -426,6 +426,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + mysql1: + dependency: transitive + description: + name: mysql1 + sha256: "68aec7003d2abc85769bafa1777af3f4a390a90c31032b89636758ff8eb839e9" + url: "https://pub.dev" + source: hosted + version: "0.20.0" node_preamble: dependency: transitive description: diff --git a/packages/dox-query-builder/lib/src/drivers/db_driver.dart b/packages/dox-query-builder/lib/src/drivers/db_driver.dart index 4086995..43bda60 100644 --- a/packages/dox-query-builder/lib/src/drivers/db_driver.dart +++ b/packages/dox-query-builder/lib/src/drivers/db_driver.dart @@ -1,14 +1,19 @@ -import 'package:postgres/postgres.dart'; +enum Driver { postgres, mysql } /// interface for database driver abstract class DBDriver { + Driver getName(); + /// run query and return map result - Future execute(String query, + Future execute(String query, {Map? substitutionValues}); /// run query and return map result - Future>> mappedResultsQuery(String query, - {Map? substitutionValues}); + Future>> mappedResultsQuery( + String query, { + String? primaryKey, + Map? substitutionValues, + }); /// run query, this function do not return any value Future query(String query, {Map? substitutionValues}); diff --git a/packages/dox-query-builder/lib/src/drivers/mysql_driver.dart b/packages/dox-query-builder/lib/src/drivers/mysql_driver.dart new file mode 100644 index 0000000..f8c71fa --- /dev/null +++ b/packages/dox-query-builder/lib/src/drivers/mysql_driver.dart @@ -0,0 +1,136 @@ +import 'dart:convert'; + +import 'package:dox_query_builder/dox_query_builder.dart'; +import 'package:mysql1/mysql1.dart'; + +/// driver for postgres SQL +/// support PostgreSQLConnection and PgPool +class MysqlDriver extends DBDriver { + final MySqlConnection conn; + + /// constructor + MysqlDriver({required this.conn}); + + @override + Driver getName() { + return Driver.mysql; + } + + /// run query and return map result + @override + Future execute( + String query, { + Map? substitutionValues, + }) async { + dynamic result = + await conn.run(query, substitutionValues: substitutionValues); + return result as T; + } + + /// run query and return map result + @override + Future>> mappedResultsQuery( + String query, { + String? primaryKey, + Map? substitutionValues, + }) async { + return await conn.execute( + query, + primaryKey: primaryKey, + substitutionValues: substitutionValues, + ); + } + + /// only run query + @override + Future query( + String query, { + Map? substitutionValues, + }) async { + await conn.run(query, substitutionValues: substitutionValues); + } +} + +/// extension on mysql connection +extension MySqlConnectionExecute on MySqlConnection { + Future>> execute( + String sql, { + String? primaryKey, + Map? substitutionValues, + }) async { + Results results = await run(sql, substitutionValues: substitutionValues); + + /// if result has last insert id, it mean insert query and need to return + /// correct id + if (results.insertId != null && + results.insertId! > 0 && + primaryKey != null) { + return >[ + {primaryKey: results.insertId} + ]; + } + + /// normal get query which need to return list of map data + /// similar to postgres + return _parseResultToMap(results); + } + + Future run( + String sql, { + Map? substitutionValues, + }) async { + List params = _substitutionValuesToList(substitutionValues); + sql = sql.replaceAll(RegExp(r'@\w+'), '?'); + return await query(sql, params); + } + + List> _parseResultToMap(Results results) { + List> result = >[]; + for (ResultRow element in results) { + Map data = {}; + element.fields.forEach((String key, dynamic value) { + /// blog need to convert to string + if (value is Blob) { + data[key] = value.toString(); + } else { + data[key] = value; + } + + /// if data is json string + if (data[key].toString().startsWith('{"')) { + try { + /// convert to map + data[key] = jsonDecode(data[key]); + } catch (error) { + /// ignore error + } + } + }); + result.add(data); + } + return result; + } + + List _substitutionValuesToList(dynamic params) { + List list = []; + if (params != null && (params as Map).isNotEmpty) { + params.forEach((String key, dynamic value) { + if (value is Map) { + value = jsonEncode(value); + } else if (value is DateTime) { + value = value.toIso8601String().split('.').first; + } else if (value.toString().endsWith('Z')) { + try { + value = DateTime.parse(value).toIso8601String().split('.').first; + } catch (error) { + value = value.toString(); + } + } else { + value = value.toString(); + } + list.add(value); + }); + } + return list; + } +} diff --git a/packages/dox-query-builder/lib/src/drivers/postgres_driver.dart b/packages/dox-query-builder/lib/src/drivers/postgres_driver.dart index b73108c..2e38948 100644 --- a/packages/dox-query-builder/lib/src/drivers/postgres_driver.dart +++ b/packages/dox-query-builder/lib/src/drivers/postgres_driver.dart @@ -9,22 +9,28 @@ class PostgresDriver extends DBDriver { /// constructor PostgresDriver({required this.conn}); + @override + Driver getName() { + return Driver.postgres; + } + /// run query and return map result @override - Future execute( + Future execute( String query, { Map? substitutionValues, }) async { Result result = await conn.runTx((TxSession s) async { return await s.execute(Sql.named(query), parameters: substitutionValues); }); - return result; + return result as T; } /// run query and return map result @override Future>> mappedResultsQuery( String query, { + String? primaryKey, Map? substitutionValues, }) async { Result result = diff --git a/packages/dox-query-builder/lib/src/insert.dart b/packages/dox-query-builder/lib/src/insert.dart index 38239a3..0a68c9a 100644 --- a/packages/dox-query-builder/lib/src/insert.dart +++ b/packages/dox-query-builder/lib/src/insert.dart @@ -1,3 +1,5 @@ +import 'package:dox_query_builder/dox_query_builder.dart'; + import 'shared_mixin.dart'; mixin Insert implements SharedMixin { @@ -29,11 +31,13 @@ mixin Insert implements SharedMixin { await insertMultiple(>[data]); if (result.isNotEmpty) { Map insertedData = result.first; - int id = insertedData[primaryKey] ?? 0; + int? id = insertedData[primaryKey]; resetSubstitutionValues(); - return await queryBuilder.find(id); + if (id != null) { + return await queryBuilder.find(id); + } } - return null; + return {}; } /// insert/create multiple records @@ -59,14 +63,18 @@ mixin Insert implements SharedMixin { List ret = []; data.forEach((String key, dynamic value) { String columnKey = helper.parseColumnKey(key); - ret.add("@$columnKey"); + ret.add(columnKey); addSubstitutionValues(columnKey, value); }); values.add("(${ret.join(',')})"); } + String returningQuery = queryBuilder.dbDriver.getName() == Driver.postgres + ? 'RETURNING $primaryKey' + : ''; + String query = - "INSERT INTO $tableName (${columns.join(',')}) VALUES ${values.join(',')} RETURNING $primaryKey"; + "INSERT INTO $tableName (${columns.join(',')}) VALUES ${values.join(',')} $returningQuery"; return await helper.runQuery(query); } } diff --git a/packages/dox-query-builder/lib/src/main.dart b/packages/dox-query-builder/lib/src/main.dart index 9969072..f3d3620 100644 --- a/packages/dox-query-builder/lib/src/main.dart +++ b/packages/dox-query-builder/lib/src/main.dart @@ -1,4 +1,6 @@ import 'package:dox_query_builder/dox_query_builder.dart'; +import 'package:dox_query_builder/src/drivers/mysql_driver.dart'; +import 'package:mysql1/mysql1.dart'; import 'package:postgres/postgres.dart'; class SqlQueryBuilder { @@ -10,7 +12,7 @@ class SqlQueryBuilder { SqlQueryBuilder._internal(); - late DBDriver db; + late DBDriver dbDriver; bool debug = true; @@ -25,12 +27,29 @@ class SqlQueryBuilder { /// ); /// ``` static void initialize({ - required Connection database, + required dynamic database, bool debug = false, QueryPrinter? printer, + Driver driver = Driver.postgres, }) { SqlQueryBuilder sql = SqlQueryBuilder(); - sql.db = PostgresDriver(conn: database); + + if (driver == Driver.postgres) { + if (database is! Connection) { + throw Exception( + 'Invalid database connection. It must be postgres `Connection` type'); + } + sql.dbDriver = PostgresDriver(conn: database); + } else if (driver == Driver.mysql) { + if (database is! MySqlConnection) { + throw Exception( + 'Invalid database connection. It must be `MySqlConnection` type'); + } + sql.dbDriver = MysqlDriver(conn: database); + } else { + throw Exception('Invalid driver or not supported'); + } + sql.debug = debug; // coverage:ignore-start if (printer != null) { diff --git a/packages/dox-query-builder/lib/src/model.dart b/packages/dox-query-builder/lib/src/model.dart index 3c264cc..2aaff92 100644 --- a/packages/dox-query-builder/lib/src/model.dart +++ b/packages/dox-query-builder/lib/src/model.dart @@ -43,16 +43,17 @@ class Model extends QueryBuilder { String? updatedAtColumn = timestampsColumn['updated_at']; Map values = toMap(removeRelations: true); + DateTime currentTime = now(); if (values[primaryKey] == null) { values.removeWhere((String key, dynamic value) => value == null); if (createdAtColumn != null) { - values[createdAtColumn] = now(); + values[createdAtColumn] = currentTime; createdAt = values[createdAtColumn]; } if (updatedAtColumn != null) { - values[updatedAtColumn] = now(); + values[updatedAtColumn] = currentTime; updatedAt = values[updatedAtColumn]; } @@ -68,7 +69,7 @@ class Model extends QueryBuilder { values.remove(createdAtColumn); values.removeWhere((String key, dynamic value) => value == null); if (updatedAtColumn != null) { - values[updatedAtColumn] = now(); + values[updatedAtColumn] = currentTime; updatedAt = values[updatedAtColumn]; } diff --git a/packages/dox-query-builder/lib/src/query_builder.dart b/packages/dox-query-builder/lib/src/query_builder.dart index 14dd79e..4f22736 100644 --- a/packages/dox-query-builder/lib/src/query_builder.dart +++ b/packages/dox-query-builder/lib/src/query_builder.dart @@ -1,5 +1,4 @@ import 'package:dox_query_builder/dox_query_builder.dart'; -import 'package:postgres/postgres.dart'; import 'count.dart'; import 'delete.dart'; @@ -43,7 +42,7 @@ class QueryBuilder String primaryKey = 'id'; @override - DBDriver get db => SqlQueryBuilder().db; + DBDriver get dbDriver => SqlQueryBuilder().dbDriver; @override QueryBuilderHelper get helper => QueryBuilderHelper(this); @@ -71,8 +70,9 @@ class QueryBuilder String tableName = ''; @override - dynamic addSubstitutionValues(String key, dynamic value) { - return _substitutionValues[key] = value; + void addSubstitutionValues(String key, dynamic value) { + String index = key.replaceFirst('@', ''); + _substitutionValues[index] = value; } @override @@ -145,12 +145,12 @@ class QueryBuilder /// ``` /// var result = await QueryBuilder.query('select * from blog where id = @id', {'id' : 1}); /// - static Future query( + static Future query( String query, { Map? substitutionValues = const {}, }) { return SqlQueryBuilder() - .db - .execute(query, substitutionValues: substitutionValues); + .dbDriver + .execute(query, substitutionValues: substitutionValues); } } diff --git a/packages/dox-query-builder/lib/src/schema.dart b/packages/dox-query-builder/lib/src/schema.dart index a489227..c421068 100644 --- a/packages/dox-query-builder/lib/src/schema.dart +++ b/packages/dox-query-builder/lib/src/schema.dart @@ -44,7 +44,7 @@ class Schema { /// ``` static Future drop(String tableName) async { await SqlQueryBuilder() - .db + .dbDriver .query("DROP TABLE IF EXISTS $tableName RESTRICT"); } } diff --git a/packages/dox-query-builder/lib/src/schema/table.dart b/packages/dox-query-builder/lib/src/schema/table.dart index 4e7ef3c..cb11ac1 100644 --- a/packages/dox-query-builder/lib/src/schema/table.dart +++ b/packages/dox-query-builder/lib/src/schema/table.dart @@ -14,7 +14,10 @@ class Table with TableUpdate { bool debug = SqlQueryBuilder().debug; @override - DBDriver get db => SqlQueryBuilder().db; + DBDriver get dbDriver => SqlQueryBuilder().dbDriver; + + String get defaultTimestampType => + dbDriver.getName() == Driver.postgres ? 'TIMESTAMPTZ' : 'TIMESTAMP'; // coverage:ignore-start @override @@ -62,7 +65,10 @@ class Table with TableUpdate { } TableColumn money(String name) { - TableColumn col = TableColumn(name: name, type: "MONEY"); + TableColumn col = TableColumn( + name: name, + type: dbDriver.getName() == Driver.postgres ? "MONEY" : "INTEGER", + ); columns.add(col); return col; } @@ -74,7 +80,9 @@ class Table with TableUpdate { } TableColumn jsonb(String name) { - TableColumn col = TableColumn(name: name, type: "JSONB"); + TableColumn col = TableColumn( + name: name, + type: dbDriver.getName() == Driver.postgres ? "JSONB" : 'JSON'); columns.add(col); return col; } @@ -123,23 +131,24 @@ class Table with TableUpdate { } TableColumn timestampTz(String name) { - TableColumn col = TableColumn(name: name, type: 'TIMESTAMPTZ'); + TableColumn col = TableColumn(name: name, type: defaultTimestampType); columns.add(col); return col; } TableColumn softDeletes([dynamic name]) { TableColumn col = - TableColumn(name: name ?? 'deleted_at', type: 'TIMESTAMPTZ').nullable(); + TableColumn(name: name ?? 'deleted_at', type: defaultTimestampType) + .nullable(); columns.add(col); return col; } void timestamps() { TableColumn createdAt = - TableColumn(name: 'created_at', type: 'TIMESTAMPTZ').nullable(); + TableColumn(name: 'created_at', type: defaultTimestampType).nullable(); TableColumn updatedAt = - TableColumn(name: 'updated_at', type: 'TIMESTAMPTZ').nullable(); + TableColumn(name: 'updated_at', type: defaultTimestampType).nullable(); columns.add(createdAt); columns.add(updatedAt); } @@ -172,6 +181,6 @@ class Table with TableUpdate { if (debug) { logger.log(query); // coverage:ignore-line } - await db.mappedResultsQuery(query); + await dbDriver.mappedResultsQuery(query); } } diff --git a/packages/dox-query-builder/lib/src/schema/table.shared_mixin.dart b/packages/dox-query-builder/lib/src/schema/table.shared_mixin.dart index 9c355f8..52ba7a4 100644 --- a/packages/dox-query-builder/lib/src/schema/table.shared_mixin.dart +++ b/packages/dox-query-builder/lib/src/schema/table.shared_mixin.dart @@ -7,6 +7,6 @@ abstract class TableSharedMixin { final List columns = []; String tableName = ''; bool debug = false; - DBDriver get db; + DBDriver get dbDriver; Logger get logger; } diff --git a/packages/dox-query-builder/lib/src/schema/table.update.dart b/packages/dox-query-builder/lib/src/schema/table.update.dart index 3624861..bf74102 100644 --- a/packages/dox-query-builder/lib/src/schema/table.update.dart +++ b/packages/dox-query-builder/lib/src/schema/table.update.dart @@ -1,3 +1,5 @@ +import 'package:dox_query_builder/dox_query_builder.dart'; + import 'table.column.dart'; import 'table.shared_mixin.dart'; @@ -44,7 +46,11 @@ mixin TableUpdate implements TableSharedMixin { /// changing type if (col.type != null) { - queries.add("ALTER COLUMN ${col.name} TYPE ${col.type}"); + if (dbDriver.getName() == Driver.mysql) { + queries.add("MODIFY COLUMN ${col.name} ${col.type}"); + } else { + queries.add("ALTER COLUMN ${col.name} TYPE ${col.type}"); + } } /// changing default value @@ -53,8 +59,13 @@ mixin TableUpdate implements TableSharedMixin { } /// set null - queries.add( - "ALTER COLUMN ${col.name} ${col.isNullable ? 'DROP NOT NULL' : 'SET NOT NULL'}"); + if (dbDriver.getName() == Driver.mysql) { + queries.add( + "MODIFY COLUMN ${col.name} ${col.isNullable ? 'NULL' : 'NOT NULL'}"); + } else { + queries.add( + "ALTER COLUMN ${col.name} ${col.isNullable ? 'DROP NOT NULL' : 'SET NOT NULL'}"); + } /// set unique if (col.isUnique) { @@ -62,21 +73,24 @@ mixin TableUpdate implements TableSharedMixin { } /// run final query - String query = "ALTER TABLE $tableName ${queries.join(',')}"; - return await _runQuery(query); + for (String q in queries) { + String query = "ALTER TABLE $tableName $q"; + return await _runQuery(query); + } } Future _runQuery(String query) async { if (debug) { logger.log(query); // coverage:ignore-line } - await db.mappedResultsQuery(query); + await dbDriver.mappedResultsQuery(query); } Future> getTableColumns() async { String query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$tableName'"; - List> result = await db.mappedResultsQuery(query); + List> result = + await dbDriver.mappedResultsQuery(query); List columns = []; diff --git a/packages/dox-query-builder/lib/src/shared_mixin.dart b/packages/dox-query-builder/lib/src/shared_mixin.dart index 225c3c8..56ae18e 100644 --- a/packages/dox-query-builder/lib/src/shared_mixin.dart +++ b/packages/dox-query-builder/lib/src/shared_mixin.dart @@ -7,7 +7,7 @@ abstract class SharedMixin { QueryBuilder get queryBuilder; QueryBuilderHelper get helper; Logger get logger; - DBDriver get db; + DBDriver get dbDriver; Map get substitutionValues; String get selectQueryString; String primaryKey = 'id'; diff --git a/packages/dox-query-builder/lib/src/truncate.dart b/packages/dox-query-builder/lib/src/truncate.dart index 59865e3..9268476 100644 --- a/packages/dox-query-builder/lib/src/truncate.dart +++ b/packages/dox-query-builder/lib/src/truncate.dart @@ -1,3 +1,5 @@ +import 'package:dox_query_builder/dox_query_builder.dart'; + import 'shared_mixin.dart'; mixin Truncate implements SharedMixin { @@ -8,7 +10,11 @@ mixin Truncate implements SharedMixin { /// await Blog().truncate(resetId: true); /// ``` Future truncate({bool resetId = true}) async { - String reset = resetId ? 'RESTART IDENTITY CASCADE' : ''; + String reset = resetId + ? queryBuilder.dbDriver.getName() == Driver.postgres + ? 'RESTART IDENTITY CASCADE' + : '' + : ''; String query = "TRUNCATE TABLE $tableName $reset"; await helper.runQuery(query); } diff --git a/packages/dox-query-builder/lib/src/update.dart b/packages/dox-query-builder/lib/src/update.dart index faa091a..c363588 100644 --- a/packages/dox-query-builder/lib/src/update.dart +++ b/packages/dox-query-builder/lib/src/update.dart @@ -14,9 +14,9 @@ mixin Update implements SharedMixin { List columnToUpdate = []; data.forEach((String column, dynamic value) { - String key = helper.parseColumnKey(column); - columnToUpdate.add("$column = @$key"); - addSubstitutionValues(key, value); + String columnKey = helper.parseColumnKey(column); + columnToUpdate.add("$column = $columnKey"); + addSubstitutionValues(columnKey, value); }); q += columnToUpdate.join(','); q += helper.getCommonQuery(); diff --git a/packages/dox-query-builder/lib/src/utils/helper.dart b/packages/dox-query-builder/lib/src/utils/helper.dart index f07d120..55c1857 100644 --- a/packages/dox-query-builder/lib/src/utils/helper.dart +++ b/packages/dox-query-builder/lib/src/utils/helper.dart @@ -6,7 +6,8 @@ class QueryBuilderHelper { String parseColumnKey(String column) { String timestamp = DateTime.now().microsecondsSinceEpoch.toString(); - return "$column$timestamp".replaceAll(RegExp(r'[^\w]'), ""); + String key = "$column$timestamp".replaceAll(RegExp(r'[^\w]'), ""); + return '@$key'; } String getCommonQuery({bool isCountQuery = false}) { @@ -27,9 +28,12 @@ class QueryBuilderHelper { Future>> runQuery(String query) async { Map values = queryBuilder.substitutionValues; if (queryBuilder.shouldDebug) queryBuilder.logger.log(query, values); - DBDriver db = queryBuilder.db; query = query.replaceAll(RegExp(' +'), ' '); - return await db.mappedResultsQuery(query, substitutionValues: values); + return await queryBuilder.dbDriver.mappedResultsQuery( + query, + substitutionValues: values, + primaryKey: queryBuilder.primaryKey, + ); } List> getMapResult( diff --git a/packages/dox-query-builder/lib/src/where.dart b/packages/dox-query-builder/lib/src/where.dart index 7a31d65..fdf6473 100644 --- a/packages/dox-query-builder/lib/src/where.dart +++ b/packages/dox-query-builder/lib/src/where.dart @@ -73,9 +73,9 @@ mixin Where implements SharedMixin { } String columnKey = helper.parseColumnKey(column); if (_wheres.isEmpty) { - _wheres.add("$column $condition @$columnKey"); + _wheres.add("$column $condition $columnKey"); } else { - _wheres.add("$type $column $condition @$columnKey"); + _wheres.add("$type $column $condition $columnKey"); } addSubstitutionValues(columnKey, value); return queryBuilder; diff --git a/packages/dox-query-builder/pubspec.yaml b/packages/dox-query-builder/pubspec.yaml index 3d6481e..eaf1ce8 100644 --- a/packages/dox-query-builder/pubspec.yaml +++ b/packages/dox-query-builder/pubspec.yaml @@ -1,5 +1,5 @@ name: dox_query_builder -description: PostgresSQL query builder, Support Model, Where, orWhere, Find, Join, softDeletes, Debugging and many mores. +description: PostgresSQL, MYSQL query builder, Support Model, Where, orWhere, Find, Join, softDeletes, Debugging and many mores. version: 2.0.0 repository: https://github.com/dartondox/dox homepage: https://dartondox.dev @@ -9,6 +9,7 @@ environment: dependencies: postgres: ^3.0.5 + mysql1: ^0.20.0 dox_annotation: ^1.0.5 dev_dependencies: diff --git a/packages/dox-query-builder/test/belongs_to_test.dart b/packages/dox-query-builder/test/belongs_to_test.dart index 116b23a..84ad553 100644 --- a/packages/dox-query-builder/test/belongs_to_test.dart +++ b/packages/dox-query-builder/test/belongs_to_test.dart @@ -6,11 +6,10 @@ import 'models/blog/blog.model.dart'; import 'models/blog_info/blog_info.model.dart'; void main() async { - SqlQueryBuilder.initialize(database: await poolConnection()); + await initQueryBuilder(); group('Belongs To |', () { setUp(() async { - SqlQueryBuilder.initialize(database: await poolConnection()); await Schema.create('blog', (Table table) { table.id('uid'); table.string('title'); diff --git a/packages/dox-query-builder/test/connection.dart b/packages/dox-query-builder/test/connection.dart index c370c4a..d6db65f 100644 --- a/packages/dox-query-builder/test/connection.dart +++ b/packages/dox-query-builder/test/connection.dart @@ -1,20 +1,29 @@ import 'dart:io'; -import 'package:postgres/postgres.dart'; +import 'package:dox_query_builder/dox_query_builder.dart'; -Future poolConnection() { - String host = Platform.environment['DB_HOST'] ?? 'localhost'; - int port = int.parse(Platform.environment['PORT'] ?? '5432'); - return Connection.open( - Endpoint( - host: host, - port: port, - database: 'postgres', - username: 'postgres', - password: 'postgres', - ), - settings: PoolSettings( - sslMode: SslMode.disable, - ), +import 'mysql.dart'; +import 'postgres.dart'; + +Future initQueryBuilder() async { + SqlQueryBuilder.initialize( + database: await poolConnection(), + driver: getDriver(), ); } + +Future poolConnection() { + if (Platform.environment['DRIVER'] == 'mysql') { + return mysqlConnection(); + } else { + return postgresConnection(); + } +} + +Driver getDriver() { + if (Platform.environment['DRIVER'] == 'mysql') { + return Driver.mysql; + } else { + return Driver.postgres; + } +} diff --git a/packages/dox-query-builder/test/has_many_test.dart b/packages/dox-query-builder/test/has_many_test.dart index 67f98d4..8e8f110 100644 --- a/packages/dox-query-builder/test/has_many_test.dart +++ b/packages/dox-query-builder/test/has_many_test.dart @@ -6,11 +6,10 @@ import 'models/blog/blog.model.dart'; import 'models/blog_info/blog_info.model.dart'; void main() async { - SqlQueryBuilder.initialize(database: await poolConnection()); + await initQueryBuilder(); group('Has One |', () { setUp(() async { - SqlQueryBuilder.initialize(database: await poolConnection()); await Schema.create('blog', (Table table) { table.id('uid'); table.string('title'); diff --git a/packages/dox-query-builder/test/has_one_test.dart b/packages/dox-query-builder/test/has_one_test.dart index 8a13ee2..3176bc1 100644 --- a/packages/dox-query-builder/test/has_one_test.dart +++ b/packages/dox-query-builder/test/has_one_test.dart @@ -6,11 +6,10 @@ import 'models/blog/blog.model.dart'; import 'models/blog_info/blog_info.model.dart'; void main() async { - SqlQueryBuilder.initialize(database: await poolConnection()); + await initQueryBuilder(); group('Has One |', () { setUp(() async { - SqlQueryBuilder.initialize(database: await poolConnection()); await Schema.create('blog', (Table table) { table.id('uid'); table.string('title'); diff --git a/packages/dox-query-builder/test/many_to_many_test.dart b/packages/dox-query-builder/test/many_to_many_test.dart index 66f7fd8..227c1f0 100644 --- a/packages/dox-query-builder/test/many_to_many_test.dart +++ b/packages/dox-query-builder/test/many_to_many_test.dart @@ -7,7 +7,7 @@ import 'models/artist_song/artist_song.model.dart'; import 'models/song/song.model.dart'; void main() async { - SqlQueryBuilder.initialize(database: await poolConnection()); + await initQueryBuilder(); group('Many To Many |', () { setUp(() async { diff --git a/packages/dox-query-builder/test/model_custom_table_name_test.dart b/packages/dox-query-builder/test/model_custom_table_name_test.dart index f61498c..6e3e303 100644 --- a/packages/dox-query-builder/test/model_custom_table_name_test.dart +++ b/packages/dox-query-builder/test/model_custom_table_name_test.dart @@ -4,10 +4,11 @@ import 'package:test/test.dart'; import 'connection.dart'; import 'models/user/user.model.dart'; -void main() { +void main() async { + await initQueryBuilder(); + group('Model with custom table name', () { setUp(() async { - SqlQueryBuilder.initialize(database: await poolConnection()); await Schema.create('users', (Table table) { table.id(); table.string('name'); diff --git a/packages/dox-query-builder/test/model_test.dart b/packages/dox-query-builder/test/model_test.dart index 1cfcdae..a499ef9 100644 --- a/packages/dox-query-builder/test/model_test.dart +++ b/packages/dox-query-builder/test/model_test.dart @@ -5,10 +5,11 @@ import 'connection.dart'; import 'models/blog/blog.model.dart'; import 'models/blog_info/blog_info.model.dart'; -void main() { +void main() async { + await initQueryBuilder(); + group('Model |', () { setUp(() async { - SqlQueryBuilder.initialize(database: await poolConnection()); await Schema.create('blog', (Table table) { table.id('uid'); table.string('title'); diff --git a/packages/dox-query-builder/test/mysql.dart b/packages/dox-query-builder/test/mysql.dart new file mode 100644 index 0000000..c2cd5cf --- /dev/null +++ b/packages/dox-query-builder/test/mysql.dart @@ -0,0 +1,17 @@ +import 'dart:io'; + +import 'package:mysql1/mysql1.dart'; + +Future mysqlConnection() { + String host = Platform.environment['DB_HOST'] ?? 'localhost'; + int port = int.parse(Platform.environment['PORT'] ?? '3306'); + String user = Platform.environment['DB_USER'] ?? 'root'; + ConnectionSettings settings = ConnectionSettings( + host: host, + port: port, + user: user, + password: 'password', + db: 'dox-framework', + ); + return MySqlConnection.connect(settings); +} diff --git a/packages/dox-query-builder/test/postgres.dart b/packages/dox-query-builder/test/postgres.dart new file mode 100644 index 0000000..797bac6 --- /dev/null +++ b/packages/dox-query-builder/test/postgres.dart @@ -0,0 +1,20 @@ +import 'dart:io'; + +import 'package:postgres/postgres.dart'; + +Future postgresConnection() { + String host = Platform.environment['DB_HOST'] ?? 'localhost'; + int port = int.parse(Platform.environment['PORT'] ?? '5432'); + return Connection.open( + Endpoint( + host: host, + port: port, + database: 'postgres', + username: 'postgres', + password: 'postgres', + ), + settings: PoolSettings( + sslMode: SslMode.disable, + ), + ); +} diff --git a/packages/dox-query-builder/test/schema_test.dart b/packages/dox-query-builder/test/schema_test.dart index 693b809..4869b03 100644 --- a/packages/dox-query-builder/test/schema_test.dart +++ b/packages/dox-query-builder/test/schema_test.dart @@ -4,11 +4,10 @@ import 'package:test/test.dart'; import 'connection.dart'; void main() async { - SqlQueryBuilder.initialize(database: await poolConnection()); + await initQueryBuilder(); group('Schema |', () { setUp(() async { - SqlQueryBuilder.initialize(database: await poolConnection()); await Schema.create('schema_test_table', (Table table) { table.id(); table.uuid('uuid'); diff --git a/packages/dox-query-builder/test/sql_query_builder_test.dart b/packages/dox-query-builder/test/sql_query_builder_test.dart index f22573a..dd0d8b3 100644 --- a/packages/dox-query-builder/test/sql_query_builder_test.dart +++ b/packages/dox-query-builder/test/sql_query_builder_test.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:dox_query_builder/dox_query_builder.dart'; +import 'package:mysql1/mysql1.dart'; import 'package:postgres/postgres.dart'; import 'package:test/test.dart'; @@ -9,7 +10,7 @@ import 'models/blog/blog.model.dart'; import 'models/blog_info/blog_info.model.dart'; void main() async { - SqlQueryBuilder.initialize(database: await poolConnection()); + await initQueryBuilder(); group('Query Builder', () { setUp(() async { @@ -131,9 +132,13 @@ void main() async { blog.description = 'Awesome blog body'; await blog.save(); - Result b = await QueryBuilder.query('select * from blog'); - - expect(b.first.toColumnMap()['title'], 'Awesome blog'); + if (getDriver() == Driver.postgres) { + Result b = await QueryBuilder.query('select * from blog'); + expect(b.first.toColumnMap()['title'], 'Awesome blog'); + } else { + Results b = await QueryBuilder.query('select * from blog'); + expect(b.first.fields['title'], 'Awesome blog'); + } }); test('group by', () async {