Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## 1.9.4-beta.1

- `DBSQLAdapterCapability`: added `statementsCache`.

- `ConditionSQLEncoder`:
- Added property `forCachedStatements`: forces generation of SQL statements suitable for caching.
- Fix resolution of parameter values present only in `EncodingContext.encodingParameters`
(i.e., generated by the encoder, not passed as normal parameters).

- New mixin `StatementCache`
- New `CachedStatement`.

- `PostgreSQLConnectionWrapper` now has support for statements cache (with `StatementCache`).

## 1.9.3

- `DBPostgreSQLAdapter`:
Expand Down
2 changes: 1 addition & 1 deletion lib/src/bones_api_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ typedef APILogger = void Function(APIRoot apiRoot, String type, String? message,
/// Bones API Library class.
class BonesAPI {
// ignore: constant_identifier_names
static const String VERSION = '1.9.3';
static const String VERSION = '1.9.4-beta.1';

static bool _boot = false;

Expand Down
26 changes: 25 additions & 1 deletion lib/src/bones_api_condition_encoder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,10 @@
if (valueAsList) {
return _resolveParameterValueImpl(value, context, valueType, true)
.resolveMapped((values) {
if (values is EncodingValue<String, Object?>) {
return values.asEncodingValueList();
}

var list = values is List
? values
: (values is Iterable ? values.toList(growable: false) : [values]);
Expand All @@ -1179,6 +1183,10 @@
if (!context.parametersPlaceholders.containsKey(valueKey)) {
return _resolveParameterValueImpl(value, context, valueType, false)
.resolveMapped((val) {
if (val is EncodingValue<String, Object?>) {
return val;
}

context.parametersPlaceholders[valueKey] ??= val;
return EncodingPlaceholder(
valueKey, valueType, placeholder, encodeEncodingPlaceholder);
Expand All @@ -1198,6 +1206,10 @@
namedParameters: context.namedParameters,
encodingParameters: context.encodingParameters);

if (paramValue is EncodingValue) {
return paramValue;
}

if (valueType != null) {
return resolveValueToType(paramValue, valueType,
valueAsList: valueAsList);
Expand Down Expand Up @@ -1273,7 +1285,7 @@
value == list.first;
} else {
throw ArgumentError(
"Can't resolve a `List` with mutiple values to a primitive type: $valueTypeInfo >> $value");
"Can't resolve a `List` with multiple values to a primitive type: $valueTypeInfo >> $value");

Check warning on line 1288 in lib/src/bones_api_condition_encoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_encoder.dart#L1288

Added line #L1288 was not covered by tests
}
}

Expand Down Expand Up @@ -1354,6 +1366,18 @@

@override
String toString() => encode.toString();

EncodingValueList<E> asEncodingValueList() {
var values = this;
if (values is EncodingValueList<E>) {
return values as EncodingValueList<E>;
} else {
return EncodingValueList(values.key, values.type, [values], (p) {
var v = values.encode;
return '( $v )' as E;

Check warning on line 1377 in lib/src/bones_api_condition_encoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_encoder.dart#L1375-L1377

Added lines #L1375 - L1377 were not covered by tests
});
}
}
}

abstract class EncodingValueResolved<E, T extends EncodingValueResolved<E, T>>
Expand Down
85 changes: 74 additions & 11 deletions lib/src/bones_api_condition_sql.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@

/// A [Condition] encoder for SQL.
class ConditionSQLEncoder extends ConditionEncoder {
/// The character used to quote identifiers in the SQL dialect.
final String sqlElementQuote;

/// If `true`, forces generation of SQL statements suitable for caching by
/// avoiding inline values and using substitution values whenever possible.
final bool forCachedStatements;

ConditionSQLEncoder(SchemeProvider super.schemeProvider,
{required this.sqlElementQuote});
{required this.sqlElementQuote, this.forCachedStatements = false});

@override
String get groupOpener => '(';
Expand All @@ -37,12 +42,26 @@

var schemeProvider = this.schemeProvider;
if (schemeProvider == null) {
var idKey = context.addEncodingParameter('id', c.idValue);
var idValue = c.idValue;

Check warning on line 45 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L45

Added line #L45 was not covered by tests

var encodingValue = valueToParameterValue(context, idValue,

Check warning on line 47 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L47

Added line #L47 was not covered by tests
fieldKey: 'id', fieldType: int);

var idKey = context.addEncodingParameter('id', encodingValue);

Check warning on line 50 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L50

Added line #L50 was not covered by tests

if (forCachedStatements) {
var c2 = KeyConditionEQ(
[ConditionKeyField('id')],
ConditionParameter.key(idKey),

Check warning on line 55 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L52-L55

Added lines #L52 - L55 were not covered by tests
);
return encodeKeyConditionEQ(c2, context);

Check warning on line 57 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L57

Added line #L57 was not covered by tests
}

var q = sqlElementQuote;
var tableKey = '$q$tableAlias$q.$q$idKey$q';

return encodeConditionValuesWithOperator(
context, int, idKey, tableKey, '=', c.idValue, false);
context, int, idKey, tableKey, '=', encodingValue, false);
} else {
var tableSchemeRet = schemeProvider.getTableScheme(tableName);

Expand All @@ -56,13 +75,26 @@

var idFieldName = tableScheme.idFieldName ?? 'id';
var idType = tableScheme.fieldsTypes[idFieldName] ?? int;
var idValue = c.idValue;

var encodingValue = valueToParameterValue(context, idValue,
fieldKey: idFieldName, fieldType: idType);

var idKey = context.addEncodingParameter(idFieldName, encodingValue);

if (forCachedStatements) {
var c2 = KeyConditionEQ(
[ConditionKeyField(idFieldName)],
ConditionParameter.key(idKey),
);
return encodeKeyConditionEQ(c2, context);
}

var idKey = context.addEncodingParameter(idFieldName, c.idValue);
var q = sqlElementQuote;
var tableKey = '$q$tableAlias$q.$q$idKey$q';

return encodeConditionValuesWithOperator(
context, idType, idKey, tableKey, '=', c.idValue, false);
context, idType, idKey, tableKey, '=', encodingValue, false);
});
}
}
Expand All @@ -75,12 +107,26 @@

var schemeProvider = this.schemeProvider;
if (schemeProvider == null) {
var idKey = context.addEncodingParameter('id', c.idsValues);
var idsValues = c.idsValues;

Check warning on line 110 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L110

Added line #L110 was not covered by tests

var encodingValue = valueToParameterValue(context, idsValues,

Check warning on line 112 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L112

Added line #L112 was not covered by tests
fieldKey: 'id', fieldType: int, valueAsList: true);

var idKey = context.addEncodingParameter('id', encodingValue);

Check warning on line 115 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L115

Added line #L115 was not covered by tests

if (forCachedStatements) {
var c2 = KeyConditionIN(
[ConditionKeyField('id')],
ConditionParameter.key(idKey),

Check warning on line 120 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L117-L120

Added lines #L117 - L120 were not covered by tests
);
return encodeKeyConditionIN(c2, context);

Check warning on line 122 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L122

Added line #L122 was not covered by tests
}

var q = sqlElementQuote;
var tableKey = '$q$tableAlias$q.$q$idKey$q';

return encodeConditionValuesWithOperator(
context, int, idKey, tableKey, 'IN', c.idsValues, true);
context, int, idKey, tableKey, 'IN', encodingValue, true);
} else {
var tableSchemeRet = schemeProvider.getTableScheme(tableName);

Expand All @@ -94,13 +140,26 @@

var idFieldName = tableScheme.idFieldName ?? 'id';
var idType = tableScheme.fieldsTypes[idFieldName] ?? int;
var idsValues = c.idsValues;

var encodingValue = valueToParameterValue(context, idsValues,
fieldKey: idFieldName, fieldType: idType, valueAsList: true);

var idKey = context.addEncodingParameter(idFieldName, encodingValue);

if (forCachedStatements) {
var c2 = KeyConditionIN(
[ConditionKeyField(idFieldName)],
ConditionParameter.key(idKey),
);
return encodeKeyConditionIN(c2, context);
}

var idKey = context.addEncodingParameter(idFieldName, c.idsValues);
var q = sqlElementQuote;
var tableKey = '$q$tableAlias$q.$q$idKey$q';

return encodeConditionValuesWithOperator(
context, idType, idFieldName, tableKey, 'IN', c.idsValues, true);
context, idType, idFieldName, tableKey, 'IN', encodingValue, true);
});
}
}
Expand Down Expand Up @@ -509,14 +568,18 @@
}
}

if (value is ConditionParameter) {
if (value is EncodingValue<String, Object?>) {
return value;
} else if (value is ConditionParameter) {
return conditionParameterToParameterValue(
value, context, fieldKey, fieldType,
valueAsList: valueAsList);
} else if (value is List &&
value.whereType<ConditionParameter>().isNotEmpty) {
var parametersValues = value.map((v) {
if (v is ConditionParameter) {
if (value is EncodingValue<String, Object?>) {

Check warning on line 580 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L580

Added line #L580 was not covered by tests
return value;
} else if (v is ConditionParameter) {

Check warning on line 582 in lib/src/bones_api_condition_sql.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_condition_sql.dart#L582

Added line #L582 was not covered by tests
return conditionParameterToParameterValue(
value, context, fieldKey, fieldType,
valueAsList: valueAsList);
Expand Down
1 change: 1 addition & 0 deletions lib/src/bones_api_entity_db_memory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class DBSQLMemoryAdapter extends DBSQLAdapter<DBSQLMemoryAdapterContext>
transactions: true,
transactionAbort: true,
tableSQL: false,
statementsCache: false,
constraintSupport: false,
multiIsolateSupport: false,
connectivity: DBAdapterCapabilityConnectivity.none),
Expand Down
1 change: 1 addition & 0 deletions lib/src/bones_api_entity_db_mysql.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class DBMySQLAdapter extends DBSQLAdapter<DBMySqlConnectionWrapper>
transactions: true,
transactionAbort: true,
tableSQL: true,
statementsCache: false,
constraintSupport: false,
multiIsolateSupport: true,
connectivity: DBAdapterCapabilityConnectivity.secureAndUnsecure),
Expand Down
40 changes: 29 additions & 11 deletions lib/src/bones_api_entity_db_postgres.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
transactions: true,
transactionAbort: true,
tableSQL: true,
statementsCache: true,
constraintSupport: true,
multiIsolateSupport: true,
connectivity: DBAdapterCapabilityConnectivity.secureAndUnsecure),
Expand Down Expand Up @@ -1093,13 +1094,13 @@

return doInsertSQL(
entityName, table, insertSQL, transaction, connection)
.resolveMapped((res) => _fixeTableSequence(transaction, entityName,
.resolveMapped((res) => _fixTableSequence(transaction, entityName,
table, idFieldName, idFieldType, connection, res));
});
});
}

FutureOr<dynamic> _fixeTableSequence(
FutureOr<dynamic> _fixTableSequence(
Transaction transaction,
String entityName,
String table,
Expand Down Expand Up @@ -1215,7 +1216,8 @@
}

/// A [DBPostgreSQLAdapter] connection wrapper.
class PostgreSQLConnectionWrapper extends DBConnectionWrapper<Session> {
class PostgreSQLConnectionWrapper extends DBConnectionWrapper<Session>
with StatementCache<Statement> {
final String? username;
final String host;
final int port;
Expand All @@ -1230,14 +1232,17 @@
return "postgresql://$username@$host:$port/$database${secure ? '?sslmode=require' : ''}";
}

Future<Result> _executeWithCachedStatement(
String sql, Map<String, dynamic>? substitutionValues) async {
var statement = await prepareSQLCached(sql);
return statement.run(substitutionValues);
}

Future<List<Map<String, dynamic>>> mappedResultsQuery(String sql,
{Map<String, dynamic>? substitutionValues}) async {
updateLastAccessTime();

var rs = await nativeConnection.execute(
Sql.named(sql),
parameters: substitutionValues,
);
var rs = await _executeWithCachedStatement(sql, substitutionValues);

var mappedResult = rs.map((e) => e.toResultsMap()).toList();

Expand All @@ -1247,20 +1252,21 @@
Future<Result> query(String sql,
{Map<String, dynamic>? substitutionValues}) async {
updateLastAccessTime();
return nativeConnection.execute(
Sql.named(sql),
parameters: substitutionValues,
);

return _executeWithCachedStatement(sql, substitutionValues);
}

Future<int> execute(String sql,
{Map<String, dynamic>? substitutionValues}) async {
updateLastAccessTime();

// Can't use `prepareSQLCached` for `ignoreRows: true`.
var rs = await nativeConnection.execute(
Sql.named(sql),
parameters: substitutionValues,
ignoreRows: true,
);

return rs.affectedRows;
}

Expand All @@ -1280,6 +1286,18 @@
);
}

@override
Future<CachedStatement<Statement>> prepareSQLCachedImpl(String sql) async {
var sqlNamed = Sql.named(sql);
var statement = await nativeConnection.prepare(sqlNamed);
return CachedStatement(statement);
}

@override

Check warning on line 1296 in lib/src/bones_api_entity_db_postgres.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_entity_db_postgres.dart#L1296

Added line #L1296 was not covered by tests
void disposeCachedStatement(Statement statement) {
statement.dispose();

Check warning on line 1298 in lib/src/bones_api_entity_db_postgres.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/bones_api_entity_db_postgres.dart#L1298

Added line #L1298 was not covered by tests
}

@override
bool isClosedImpl() {
final nativeConnection = this.nativeConnection;
Expand Down
Loading