classPgTokenizer
final class PgTokenizer
Reads PostgreSQL text back into the parser tokens the server would produce.
This is the inverse of realization and the reason realization can be trusted: SQL written from a terminal sequence is read back here, and the two sequences must agree. PostgreSQL's rules are its own — a string may be spelled six ways, block comments nest, and an operator is the longest run of operator characters that does not start a comment — so a tokenizer that merely looked plausible would agree with a generator that was equally wrong.
Methods§
public function __construct(
private array<string, string> $keywordTokens,
PgLookahead $lookahead,
)Parameters
$keywordTokens | array<string, string> | Terminal name by upper-cased keyword |
$lookahead | PgLookahead | Substitutions the parser frontend makes |
public function tokenize(string $sql): list<string>Reads SQL text into the tokens PostgreSQL's own lexer would produce.
Parameters
$sql | string | Text to read |
Returns
list<string> Parser token names, in orderThrows
LexicalException When the text holds something the lexer cannot readTest cases 4
Called from 1
Calls 6
- function-call
strlenline 56 - method-call PgTokenizer::skipTrivia() line 61
- static-call LexicalException::noProgress() line 63
- method-call PgTokenizer::tokenAt() line 69
- static-call LexicalException::unsupportedInput() line 71
- method-call PgLookahead::applied() line 81
public function tokenAt(string $sql, int &$offset): string|nullReads the one token that starts at the offset, advancing past it.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
string|null The token name, or null when nothing here is a tokenThrows
LexicalException When a quoted or dollar-quoted run never closesTest cases 1
Called from 1
Calls 5
- method-call PgTokenizer::quotedTokenAt() line 96
- method-call PgTokenizer::dollarTokenAt() line 97
- method-call PgTokenizer::numericTokenAt() line 98
- method-call PgTokenizer::wordTokenAt() line 99
- method-call PgTokenizer::operatorTokenAt() line 100
public function quotedTokenAt(string $sql, int &$offset): string|nullReads a quoted identifier or string in any of the prefixes PostgreSQL accepts.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
string|null The token name, or null when no quoted run opens hereThrows
LexicalException When the run never closesTest cases 1
Called from 1
Calls 2
- method-call PgTokenizer::skipQuoted() line 116
- function-call
preg_matchline 130
public function dollarTokenAt(string $sql, int &$offset): string|nullReads a dollar-quoted string or a positional parameter.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
string|null The token name, or null when neither starts hereThrows
LexicalException When a dollar-quoted string never closesTest cases 1
Called from 1
Calls 4
- function-call
preg_matchline 155 - function-call
strposline 157 - function-call
strlenline 157 - static-call LexicalException::unterminatedDollarQuotedString() line 159
public function numericTokenAt(string $sql, int &$offset): string|nullReads an integer or a float literal.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
string|null The token name, or null when no number starts hereTest cases 1
Called from 1
public function wordTokenAt(string $sql, int &$offset): string|nullReads a keyword or an unquoted identifier.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
string|null The token name, or null when no word starts hereTest cases 1
Called from 1
public function operatorTokenAt(string $sql, int &$offset): string|nullReads an operator or a single punctuation character.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
string|null The token name, or null when nothing operator-like starts hereTest cases 1
Called from 1
Calls 2
- method-call PgTokenizer::operatorAt() line 231
- function-call
strlenline 236
public function skipTrivia(string $sql, int &$offset): boolConsumes whitespace or one comment.
PostgreSQL nests block comments, so the depth is counted rather than the first */ being taken as the end.
Parameters
$sql | string | Text being read |
$offset | int | Where to read from; moved past what was consumed |
Returns
bool True when something was skippedThrows
LexicalException When a block comment never closesTest cases 2
Called from 1
public function skipQuoted(string $sql, int &$offset, string $quote): voidConsumes a quoted run, doubling being the way a quote escapes itself.
Parameters
$sql | string | Text being read |
$offset | int | Offset of the opening quote; moved past the closing one |
$quote | string | The quote character |
Throws
LexicalException When the run never closesTest cases 2
Called from 3
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::quotedTokenAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:116
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::quotedTokenAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:122
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::quotedTokenAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:135
Calls 2
- function-call
strlenline 305 - static-call LexicalException::unterminatedQuotedToken() line 322
public function operatorAt(string $sql, int $offset): array{string, string}|nullReports the operator written at the offset and the token it stands for.
A user-defined operator is the longest run of operator characters, but the run stops where a comment would start: +/* is a plus and a comment, not a three-character operator.
Parameters
$sql | string | Text being read |
$offset | int | Where to look |
Returns
array{string, string}|null The operator and its token, or null when there is noneTest cases 3
Called from 1
Calls 10
- class-const PgTokenizer::PUNCTUATION_PAIRS() line 339
- function-call
substrline 340 - function-call
strlenline 340 - method-call PgTokenizer::fixedOperator() line 341
- function-call
str_containsline 346 - class-const PgTokenizer::OPERATOR_CHARACTERS() line 346
- method-call PgTokenizer::isPunctuation() line 347
- function-call
in_arrayline 355 - function-call
preg_matchline 362 - function-call
rtrimline 363
public function fixedOperator(string $operator): string|nullReports the token a spelling with a name of its own stands for.
Parameters
$operator | string | Operator as written |
Returns
string|null The token name, or null when the operator has noneTest cases 1
Called from 2
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::operatorAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:341
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::operatorAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:369
public function isPunctuation(string $character): boolReports whether a single character is punctuation that stands for itself.
Parameters
$character | string | Character to inspect |
Returns
bool True for a punctuation characterTest cases 1
Called from 2
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::operatorAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:347
- method-call SqlFaker\PostgreSql\Generation\Tokenization\PgTokenizer::operatorAt() packages/sql-faker/src/PostgreSql/Generation/Tokenization/PgTokenizer.php:374
Calls 3
- function-call
strlenline 409 - function-call
str_containsline 409 - class-const PgTokenizer::PUNCTUATION() line 409
Private surface 5§
Implementation details, listed for orientation only.
private const OPERATOR_CHARACTERS = '+-*/<>=~!@#%^&|`?'private const PUNCTUATION = '%()*+,-./:;<=>[]^'private const PUNCTUATION_PAIRS = ['::', '..', ':=']private PgLookahead $lookaheadprivate array<string, string> $keywordTokensTest cases 25§
Test cases that cover or call this symbol, from the coverage report and from the analyzed test sources.
Dedicated tests 19
PgTokenizerTest::testDollarTokenAtLeavesADollarThatOpensNeithercallsPgTokenizerTest::testFixedOperatorNamesTheOperatorsThatHaveTokenscallsPgTokenizerTest::testIsPunctuationAcceptsASingleGrammarCharactercallsPgTokenizerTest::testNumericTokenAtLeavesTextThatIsNoNumbercallsPgTokenizerTest::testOperatorAtReportsNothingForAWordcallsPgTokenizerTest::testOperatorAtReportsPunctuationAsItselfcallsPgTokenizerTest::testOperatorAtTakesTheLongestRunOfOperatorCharacterscallsPgTokenizerTest::testOperatorTokenAtLeavesTextThatIsNoOperatorcallsPgTokenizerTest::testQuotedTokenAtLeavesAnythingThatOpensNoQuotecallsPgTokenizerTest::testSkipQuotedReportsARunThatNeverClosescallsPgTokenizerTest::testSkipQuotedTreatsADoubledQuoteAsAnEscapecallsPgTokenizerTest::testSkipTriviaReportsAnUnterminatedBlockCommentcallsPgTokenizerTest::testSkipTriviaReportsWhenNothingWasSkippedcallsPgTokenizerTest::testTokenAtReadsTheFirstTokenAndAdvancesPastItcallsPgTokenizerTest::testTokenizeAppliesTheLookaheadSubstitutioncallsPgTokenizerTest::testTokenizeReadsTextAsTheServerWouldcallsPgTokenizerTest::testTokenizeReportsAnUnterminatedDollarQuotedStringcallsPgTokenizerTest::testTokenizeReportsTextItCannotReadcallsPgTokenizerTest::testWordTokenAtLeavesTextThatIsNoWordcalls
Other tests reaching this symbol 6
MySqlProviderTestcallsGenerationContextTestcallsLexicalGrammarTestcallsSqlGeneratorFactoryTestcallsPostgreSqlProviderTestcallsSqliteProviderTestcalls