![]() |
VOOZH | about |
This page provides an overview of how LINQ queries are translated to PostgreSQL SQL in the Npgsql EF Core provider. The translation pipeline converts .NET expression trees into PostgreSQL-specific SQL statements.
For CLR-to-PostgreSQL type mappings, see Type Mapping System. For method-specific translations, see Expression Translation. For SQL rendering, see SQL Generation.
The query translation pipeline consists of three main stages:
NpgsqlSqlTranslatingExpressionVisitor converts LINQ expression trees into SQL expression nodes.NpgsqlSqlExpressionFactory creates PostgreSQL-specific SQL expression types.NpgsqlQuerySqlGenerator renders SQL expression trees into SQL text.The provider supports PostgreSQL-specific features including arrays, JSON, full-text search, regular expressions, network types, range types, and custom operators.
Sources:
The NpgsqlSqlTranslatingExpressionVisitor class extends RelationalSqlTranslatingExpressionVisitor to translate LINQ expressions into SQL expressions. It overrides key visitor methods to handle PostgreSQL-specific translations:
| Method | Purpose | Example |
|---|---|---|
VisitConditional | Translates ternary expressions; detects NULLIF patterns | a == b ? null : a → NULLIF(a, b) |
VisitUnary | Handles array length, type conversions, and ITuple unwrapping | (ITuple)tuple → unwrapped expression |
VisitBinary | Translates binary operations with PostgreSQL operators | Date subtraction → INTERVAL |
Sources:
The NpgsqlSqlExpressionFactory creates PostgreSQL-specific SQL expression nodes. It provides factory methods for expressions that don't exist in standard SQL:
RegexMatch() - Creates PgRegexMatchExpression for ~ operator src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs39-43Any() / All() - Creates PgAnyExpression / PgAllExpression for = ANY(array) / LIKE ALL(array) src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs48-61ArrayIndex() - Creates PgArrayIndexExpression for array[index] src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs66-80JsonTraversal() - Creates PgJsonTraversalExpression for -> / ->> src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs149-154ILike() - Creates PgILikeExpression for case-insensitive pattern matching src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs140-144AtTimeZone() - Creates AtTimeZoneExpression for timestamp conversion src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs106-134The provider defines custom expression types which are visited by the SQL generator:
Sources:
The provider uses translator providers to register specific .NET method and property translations:
Method Call Translators (via NpgsqlMethodCallTranslatorProvider):
Registers translators for string, DateTime, Math, Regex, Json, Arrays, Network, and more src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs43-67
Member Translators (via NpgsqlMemberTranslatorProvider):
Registers translators for members of DateTime, TimeSpan, string, BigInteger, and Range types src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMemberTranslatorProvider.cs37-49
The query translation system integrates with NpgsqlTypeMappingSource to ensure correct type handling. The NpgsqlSqlExpressionFactory uses the mapping source to find appropriate store types for expressions like AtTimeZone src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs112-127
String methods translate to PostgreSQL string functions or pattern matching. For example, Contains can translate to LIKE with wildcards test/EFCore.PG.FunctionalTests/Query/NorthwindMiscellaneousQueryNpgsqlTest.cs25-30
Arrays translate to PostgreSQL native array operations. Contains on a local collection often translates to = ANY (@parameter) test/EFCore.PG.FunctionalTests/Query/NorthwindAggregateOperatorsQueryNpgsqlTest.cs57-60
The NpgsqlQuerySqlGenerator overrides GetOperator() to translate operators to PostgreSQL equivalents:
| Expression Type | Context | PostgreSQL Operator | Notes |
|---|---|---|---|
Add | String / TsVector / Array | ` | |
ExclusiveOr | Boolean | <> | Logical XOR src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs137 |
ExclusiveOr | Numeric | # | Bitwise XOR src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs138 |
The generator handles PostgreSQL-specific syntax:
LIMIT and OFFSET clauses src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs89-113NULLS FIRST / NULLS LAST src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs144-154ValueTuple and Tuple to row value syntax (a, b) > (c, d) test/EFCore.PG.FunctionalTests/Query/NorthwindWhereQueryNpgsqlTest.cs123-127PostgreSQL supports tuple comparisons. The provider handles these via PgRowValueExpression and NpgsqlSqlNullabilityProcessor, which manages the complex null semantics required for row comparisons src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs45-127
Translates components like DayOfWeek using PostgreSQL date_part test/EFCore.PG.FunctionalTests/Query/NorthwindSelectQueryNpgsqlTest.cs17-22 and supports interval arithmetic for AddYears or TimeSpan subtraction test/EFCore.PG.FunctionalTests/Query/NorthwindMiscellaneousQueryNpgsqlTest.cs33-81
For more specific details on the translation pipeline, see:
NpgsqlQuerySqlGenerator and SQL rendering.Refresh this wiki