From 2a0c78fb0b53a5abfc5d7d119509506d11b62f2e Mon Sep 17 00:00:00 2001 From: Xavier H Date: Tue, 27 Feb 2024 14:22:55 +0100 Subject: [PATCH] Use ':' (colon) for substitution and cast operator together --- lib/src/v3/variable_tokenizer.dart | 96 ++++++++++++++++-------------- test/variable_tokenizer_test.dart | 21 +++++++ 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/lib/src/v3/variable_tokenizer.dart b/lib/src/v3/variable_tokenizer.dart index 48f792e3..6fd4efc4 100644 --- a/lib/src/v3/variable_tokenizer.dart +++ b/lib/src/v3/variable_tokenizer.dart @@ -126,55 +126,59 @@ class VariableTokenizer { final startIndex = _index; final charCode = _consume(); - if (charCode == _variableCodeUnit) { - _startVariable(startIndex); - continue nextToken; - } else { - switch (charCode) { - case $slash: - // `/*`, block comment - if (_consumeIfMatches($asterisk)) { - _blockComment(); - continue nextToken; - } - break; - case $minus: - // `--`, line comment - if (_consumeIfMatches($minus)) { - _lineComment(); - continue nextToken; - } - break; - case $doubleQuote: - // Double quote has already been consumed, but is part of the identifier - // Note that this also handles identifiers with unicode escape - // sequences like `u&"\u1234"` because the `u` and the ampersand are - // not interpreted in any other way and we skip to the next double - // quote either way. - _rewrittenSql.writeCharCode($doubleQuote); - _continueEscapedIdentifier(); + switch (charCode) { + case $slash: + // `/*`, block comment + if (_consumeIfMatches($asterisk)) { + _blockComment(); + continue nextToken; + } + break; + case $minus: + // `--`, line comment + if (_consumeIfMatches($minus)) { + _lineComment(); continue nextToken; - case $singleQuote: - // Write start of string that has already been consumed - _rewrittenSql.writeCharCode($singleQuote); - _continueStringLiteral(enableEscapes: false); + } + break; + case $doubleQuote: + // Double quote has already been consumed, but is part of the identifier + // Note that this also handles identifiers with unicode escape + // sequences like `u&"\u1234"` because the `u` and the ampersand are + // not interpreted in any other way and we skip to the next double + // quote either way. + _rewrittenSql.writeCharCode($doubleQuote); + _continueEscapedIdentifier(); + continue nextToken; + case $singleQuote: + // Write start of string that has already been consumed + _rewrittenSql.writeCharCode($singleQuote); + _continueStringLiteral(enableEscapes: false); + continue nextToken; + case $dollar: + _rewrittenSql.writeCharCode($dollar); + _potentialDollarQuotedString(); + continue nextToken; + case $e: + case $E: + if (_consumeIfMatches($singleQuote)) { + // https://cloud.google.com/spanner/docs/reference/postgresql/lexical#string_constants_with_c-style_escapes + _rewrittenSql + ..writeCharCode(charCode) // e or E + ..writeCharCode($singleQuote); + + _continueStringLiteral(enableEscapes: true); continue nextToken; - case $dollar: - _rewrittenSql.writeCharCode($dollar); - _potentialDollarQuotedString(); + } + default: + // `::`, cast operator + if (charCode == $colon && _consumeIfMatches($colon)) { + _rewrittenSql.write('::'); continue nextToken; - case $e: - case $E: - if (_consumeIfMatches($singleQuote)) { - // https://cloud.google.com/spanner/docs/reference/postgresql/lexical#string_constants_with_c-style_escapes - _rewrittenSql - ..writeCharCode(charCode) // e or E - ..writeCharCode($singleQuote); - - _continueStringLiteral(enableEscapes: true); - continue nextToken; - } - } + } else if (charCode == _variableCodeUnit) { + _startVariable(startIndex); + continue nextToken; + } } // This char has no special meaning, add it to the SQL string. diff --git a/test/variable_tokenizer_test.dart b/test/variable_tokenizer_test.dart index d2013a91..0f40777f 100644 --- a/test/variable_tokenizer_test.dart +++ b/test/variable_tokenizer_test.dart @@ -104,6 +104,27 @@ void main() { expect(desc.parameterTypes, [Type.bigInteger]); }); + group('can use : substitution symbol and cast operator together', () { + test('simple', () { + final desc = InternalQueryDescription.named( + 'SELECT id::text FROM foo WHERE a = :x:int8::int', + substitution: ':'); + expect( + desc.transformedSql, r'SELECT id::text FROM foo WHERE a = $1::int'); + expect(desc.namedVariables?.keys, ['x']); + expect(desc.parameterTypes, [Type.bigInteger]); + }); + test('with comment', () { + final desc = InternalQueryDescription.named( + 'SELECT id /**/ :: /**/ text, b::\nint8 FROM foo WHERE a = :x:int8/**/::/**/int8', + substitution: ':'); + expect(desc.transformedSql, + 'SELECT id :: text, b::\nint8 FROM foo WHERE a = \$1::int8'); + expect(desc.namedVariables?.keys, ['x']); + expect(desc.parameterTypes, [Type.bigInteger]); + }); + }); + test('finds correct end for string literal', () { final desc = InternalQueryDescription.named(r"SELECT e'@a\\' @b"); expect(desc.transformedSql, r"SELECT e'@a\\' $1");