Escape postgres keywords in generated SQL

This commit is contained in:
Simon Binder 2023-02-05 14:28:19 +01:00
parent ed964a7bf2
commit deb1b91d41
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
7 changed files with 32 additions and 10 deletions

View File

@ -82,6 +82,16 @@ class SqlWriter extends NodeSqlBuilder {
return _out.toString(); return _out.toString();
} }
@override
bool isKeyword(String lexeme) {
switch (options.effectiveDialect) {
case SqlDialect.postgres:
return isKeywordLexeme(lexeme) || isPostgresKeywordLexeme(lexeme);
default:
return isKeywordLexeme(lexeme);
}
}
FoundVariable? _findMoorVar(Variable target) { FoundVariable? _findMoorVar(Variable target) {
return query!.variables.firstWhereOrNull( return query!.variables.firstWhereOrNull(
(f) => f.variable.resolvedIndex == target.resolvedIndex); (f) => f.variable.resolvedIndex == target.resolvedIndex);

View File

@ -1,6 +1,6 @@
name: drift_dev name: drift_dev
description: Dev-dependency for users of drift. Contains the generator and development tools. description: Dev-dependency for users of drift. Contains the generator and development tools.
version: 2.5.1 version: 2.6.0-dev
repository: https://github.com/simolus3/drift repository: https://github.com/simolus3/drift
homepage: https://drift.simonbinder.eu/ homepage: https://drift.simonbinder.eu/
issue_tracker: https://github.com/simolus3/drift/issues issue_tracker: https://github.com/simolus3/drift/issues
@ -27,7 +27,7 @@ dependencies:
# Drift-specific analysis and apis # Drift-specific analysis and apis
drift: '>=2.5.0 <2.6.0' drift: '>=2.5.0 <2.6.0'
sqlite3: '>=0.1.6 <2.0.0' sqlite3: '>=0.1.6 <2.0.0'
sqlparser: '^0.27.0' sqlparser: '^0.28.0'
# Dart analysis # Dart analysis
analyzer: ^5.2.0 analyzer: ^5.2.0

View File

@ -1,3 +1,4 @@
import 'package:drift/drift.dart';
import 'package:drift_dev/src/analysis/options.dart'; import 'package:drift_dev/src/analysis/options.dart';
import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/analysis/results/results.dart';
import 'package:drift_dev/src/writer/queries/sql_writer.dart'; import 'package:drift_dev/src/writer/queries/sql_writer.dart';
@ -5,14 +6,14 @@ import 'package:sqlparser/sqlparser.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
void check(String sql, String expectedDart) { void check(String sql, String expectedDart,
{DriftOptions options = const DriftOptions.defaults()}) {
final engine = SqlEngine(); final engine = SqlEngine();
final context = engine.analyze(sql); final context = engine.analyze(sql);
final query = SqlSelectQuery('name', context, context.root, [], [], final query = SqlSelectQuery('name', context, context.root, [], [],
InferredResultSet(null, []), null, null); InferredResultSet(null, []), null, null);
final result = final result = SqlWriter(options, query: query).write();
SqlWriter(const DriftOptions.defaults(), query: query).write();
expect(result, expectedDart); expect(result, expectedDart);
} }
@ -28,4 +29,11 @@ void main() {
test('escapes Dart characters in SQL', () { test('escapes Dart characters in SQL', () {
check(r"SELECT '$hey';", r"'SELECT \'\$hey\''"); check(r"SELECT '$hey';", r"'SELECT \'\$hey\''");
}); });
test('escapes postgres keywords', () {
check('SELECT * FROM user', "'SELECT * FROM user'");
check('SELECT * FROM user', "'SELECT * FROM \"user\"'",
options: DriftOptions.defaults(
dialect: DialectOptions(SqlDialect.postgres, null)));
});
} }

View File

@ -69,7 +69,7 @@ class PreferenceConverter extends NullAwareTypeConverter<Preferences, String> {
'ORDER BY (SELECT COUNT(*) FROM friendships ' 'ORDER BY (SELECT COUNT(*) FROM friendships '
'WHERE first_user = u.id OR second_user = u.id) DESC LIMIT :amount', 'WHERE first_user = u.id OR second_user = u.id) DESC LIMIT :amount',
'amountOfGoodFriends': 'SELECT COUNT(*) FROM friendships f WHERE ' 'amountOfGoodFriends': 'SELECT COUNT(*) FROM friendships f WHERE '
'f.really_good_friends = 1 AND ' 'f.really_good_friends = TRUE AND '
'(f.first_user = :user OR f.second_user = :user)', '(f.first_user = :user OR f.second_user = :user)',
'friendshipsOf': ''' SELECT 'friendshipsOf': ''' SELECT
f.really_good_friends, "user".** f.really_good_friends, "user".**

View File

@ -555,7 +555,7 @@ abstract class _$Database extends GeneratedDatabase {
Selectable<int> amountOfGoodFriends(int user) { Selectable<int> amountOfGoodFriends(int user) {
return customSelect( return customSelect(
'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = 1 AND(f.first_user = @1 OR f.second_user = @1)', 'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = TRUE AND(f.first_user = @1 OR f.second_user = @1)',
variables: [ variables: [
Variable<int>(user) Variable<int>(user)
], ],
@ -566,7 +566,7 @@ abstract class _$Database extends GeneratedDatabase {
Selectable<FriendshipsOfResult> friendshipsOf(int user) { Selectable<FriendshipsOfResult> friendshipsOf(int user) {
return customSelect( return customSelect(
'SELECT f.really_good_friends,"user"."id" AS "nested_0.id", "user"."name" AS "nested_0.name", "user"."birth_date" AS "nested_0.birth_date", "user"."profile_picture" AS "nested_0.profile_picture", "user"."preferences" AS "nested_0.preferences" FROM friendships AS f INNER JOIN users AS user ON user.id IN (f.first_user, f.second_user) AND user.id != @1 WHERE(f.first_user = @1 OR f.second_user = @1)', 'SELECT f.really_good_friends,"user"."id" AS "nested_0.id", "user"."name" AS "nested_0.name", "user"."birth_date" AS "nested_0.birth_date", "user"."profile_picture" AS "nested_0.profile_picture", "user"."preferences" AS "nested_0.preferences" FROM friendships AS f INNER JOIN users AS "user" ON "user".id IN (f.first_user, f.second_user) AND "user".id != @1 WHERE(f.first_user = @1 OR f.second_user = @1)',
variables: [ variables: [
Variable<int>(user) Variable<int>(user)
], ],

View File

@ -24,6 +24,10 @@ class NodeSqlBuilder extends AstVisitor<void, void> {
} }
} }
bool isKeyword(String lexeme) {
return isKeywordLexeme(lexeme);
}
@override @override
void visitAggregateFunctionInvocation( void visitAggregateFunctionInvocation(
AggregateFunctionInvocation e, void arg) { AggregateFunctionInvocation e, void arg) {
@ -1321,7 +1325,7 @@ class NodeSqlBuilder extends AstVisitor<void, void> {
/// Writes an identifier, escaping it if necessary. /// Writes an identifier, escaping it if necessary.
void identifier(String identifier, void identifier(String identifier,
{bool spaceBefore = true, bool spaceAfter = true}) { {bool spaceBefore = true, bool spaceAfter = true}) {
if (isKeywordLexeme(identifier) || _notAKeywordRegex.hasMatch(identifier)) { if (isKeyword(identifier) || _notAKeywordRegex.hasMatch(identifier)) {
identifier = '"$identifier"'; identifier = '"$identifier"';
} }

View File

@ -1,6 +1,6 @@
name: sqlparser name: sqlparser
description: Parses sqlite statements and performs static analysis on them description: Parses sqlite statements and performs static analysis on them
version: 0.27.0 version: 0.28.0-dev
homepage: https://github.com/simolus3/drift/tree/develop/sqlparser homepage: https://github.com/simolus3/drift/tree/develop/sqlparser
repository: https://github.com/simolus3/drift repository: https://github.com/simolus3/drift
#homepage: https://drift.simonbinder.eu/ #homepage: https://drift.simonbinder.eu/