---
title: "Relational Query Engine"
description: "The Relational Query Engine serves as Drizzle's high-level declarative query layer, translating nested TypeScript query definitions into optimized, dialect-specific SQL execution plans across multi..."
last_updated: "2026-10-12T08:14:00.746407+00:00"
canonical_url: "https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/core-architecture/relational-query-engine"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [drizzle-orm/src/gel-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/dialect.ts)
- [drizzle-orm/src/mysql-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/dialect.ts)
- [drizzle-orm/src/pg-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/dialect.ts)
- [drizzle-orm/src/singlestore-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/dialect.ts)
- [drizzle-orm/src/sqlite-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/dialect.ts)
- [drizzle-orm/src/relations.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/relations.ts)
- [drizzle-kit/src/introspect-pg.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/introspect-pg.ts)
- [drizzle-orm/src/pg-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts)
- [drizzle-orm/src/mysql-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts)
- [drizzle-orm/src/gel-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/query.ts)
- [drizzle-orm/src/singlestore-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/query.ts)
- [drizzle-orm/src/sqlite-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts)
- [drizzle-kit/src/serializer/studio.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/serializer/studio.ts)
- [drizzle-orm/type-tests/geldb/db-rel.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/type-tests/geldb/db-rel.ts)
- [drizzle-orm/type-tests/mysql/db-rel.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/type-tests/mysql/db-rel.ts)
- [drizzle-orm/type-tests/pg/db-rel.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/type-tests/pg/db-rel.ts)
- [drizzle-orm/src/operations.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/operations.ts)
- [drizzle-orm/src/selection-proxy.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/selection-proxy.ts)
</details>

## Overview

The Relational Query Engine serves as Drizzle's high-level declarative query layer, translating nested TypeScript query definitions into optimized, dialect-specific SQL execution plans across multiple database engines. It solves the complexity of querying graph-shaped relational data structures by bridging typed schema definitions with underlying driver sessions through dedicated query builder classes like `PgRelationalQuery`, `MySqlRelationalQuery`, `GelRelationalQuery`, `SingleStoreRelationalQuery`, and `SQLiteRelationalQuery`. Key architectural decisions include encapsulating query promises and execution lifecycles, supporting nested filtering, pagination, and sorting via `findMany` and `findFirst`, and normalizing graph relations across primary and non-primary key constraints. It interacts closely with underlying database dialects to construct lateral subqueries, manage table aliases, apply selection masks, and execute row-mapping transformations on fetched database records.

Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:19-110](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L19-L110), [drizzle-orm/src/mysql-core/query-builders/query.ts:23-74](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L23-L74), [drizzle-orm/src/gel-core/query-builders/query.ts:19-63](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/query.ts#L19-L63), [drizzle-orm/src/singlestore-core/query-builders/query.ts:22-70](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/query.ts#L22-L70), [drizzle-orm/src/sqlite-core/query-builders/query.ts:22-96](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L22-L96)

## Relation Declarations and Metadata Modeling

### Schema Definition and Helpers

The relational metadata layer begins with public schema definition helpers and typed relation descriptors. The `relations` function accepts a table and a callback yielding a record of `Relation` objects, automatically binding each entry to its field name via `value.withFieldName(key)`. The helpers `createOne` and `createMany` construct `One` and `Many` relation descriptors respectively, where `createOne` evaluates nullability by inspecting whether all component fields have `notNull: true`.

```typescript
export function relations<
	TTableName extends string,
	TRelations extends Record<string, Relation<any>>,
>(
	table: AnyTable<{ name: TTableName }>,
	relations: (helpers: TableRelationsHelpers<TTableName>) => TRelations,
): Relations<TTableName, TRelations> {
	return new Relations<TTableName, TRelations>(
		table,
		(helpers: TableRelationsHelpers<TTableName>) =>
			Object.fromEntries(
				Object.entries(relations(helpers)).map(([key, value]) => [
					key,
					value.withFieldName(key),
				]),
			) as TRelations,
	);
}
```
Sources: [drizzle-orm/src/relations.ts:502-519](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/relations.ts#L502-L519)

The engine extracts complete relational schemas using `extractTablesRelationalConfig`, which iterates over raw schema entries, normalizes single-default schema wrappers, and resolves forward references through `relationsBuffer` when relations are declared before their target tables.

| Configuration Field | Type | Purpose |
| :--- | :--- | :--- |
| `tsName` | `string` | TypeScript schema object key for the table |
| `dbName` | `string` | Physical database table identifier name |
| `schema` | `string \| undefined` | Optional database schema namespace |
| `columns` | `Record<string, AnyColumn>` | Mapped column definitions |
| `relations` | `Record<string, Relation>` | Registered table relations |
| `primaryKey` | `AnyColumn[]` | Resolved primary key column array |

Sources: [drizzle-orm/src/relations.ts:416-500](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/relations.ts#L416-L500)

> [!NOTE]
> When schema entries contain a single `'default'` property holding a nested record, `extractTablesRelationalConfig` automatically unwraps it before iterating over table and relation definitions.
> Sources: [drizzle-orm/src/relations.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/relations.ts#L422-L428)

### Graph Relation Normalization

Relation graph normalization resolves mapping fields between source and target tables when explicit join configurations are omitted. The `normalizeRelation` function inspects whether a relation is an instance of `One` containing an explicit `RelationConfig`. If explicit fields and references are present, they are returned directly. Otherwise, the engine looks up the referenced table configuration in the schema and searches for matching reverse relations.

```typescript
export function normalizeRelation(
	schema: TablesRelationalConfig,
	tableNamesMap: Record<string, string>,
	relation: Relation,
): NormalizedRelation {
	if (is(relation, One) && relation.config) {
		return {
			fields: relation.config.fields,
			references: relation.config.references,
		};
	}
...
```
Sources: [drizzle-orm/src/relations.ts:559-569](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/relations.ts#L559-L569)

Reverse relation matching evaluates either matching explicit `relationName` properties or linking back via `referencedTable === relation.sourceTable`. If multiple reverse relations match without a disambiguating name, an error is thrown. When a single reverse `One` relation is found, its fields and references are inverted to establish the normalized connection path.

> [!WARNING]
> Omitting an explicit `relationName` when multiple bidirectional relations exist between the same two tables causes `normalizeRelation` to throw an ambiguous match error.
> Sources: [drizzle-orm/src/relations.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/relations.ts#L608-L618)

## Relational Query Builder Classes

### Relational Query Builder Construction and Methods

Dialect-specific relational query builders coordinate schema definitions, table mappings, and session handlers to instantiate executable queries. Each dialect exports a `RelationalQueryBuilder` class that exposes `findMany` and `findFirst` methods accepting type-safe configuration objects restricted by `KnownKeysOnly`. The `findFirst` method wraps the query configuration by setting `limit: 1` and marks the internal execution mode as `'first'`, whereas `findMany` defaults to `'many'`.

| Dialect Module | Builder Class Name | Query Runner Class Name | Session Type |
| :--- | :--- | :--- | :--- |
| PostgreSQL (`pg-core`) | `RelationalQueryBuilder` | `PgRelationalQuery` | `PgSession` |
| MySQL (`mysql-core`) | `RelationalQueryBuilder` | `MySqlRelationalQuery` | `MySqlSession` |
| Gel (`gel-core`) | `RelationalQueryBuilder` | `GelRelationalQuery` | `GelSession` |
| SingleStore (`singlestore-core`) | `RelationalQueryBuilder` | `SingleStoreRelationalQuery` | `SingleStoreSession` |
| SQLite (`sqlite-core`) | `RelationalQueryBuilder` | `SQLiteRelationalQuery` / `SQLiteSyncRelationalQuery` | `SQLiteSession` |

Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:19-63](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L19-L63), [drizzle-orm/src/mysql-core/query-builders/query.ts:23-74](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L23-L74), [drizzle-orm/src/gel-core/query-builders/query.ts:19-63](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/query.ts#L19-L63), [drizzle-orm/src/singlestore-core/query-builders/query.ts:22-70](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/query.ts#L22-L70), [drizzle-orm/src/sqlite-core/query-builders/query.ts:22-96](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L22-L96)

```typescript
findMany<TConfig extends DBQueryConfig<'many', true, TSchema, TFields>>(
	config?: KnownKeysOnly<TConfig, DBQueryConfig<'many', true, TSchema, TFields>>,
): PgRelationalQuery<BuildQueryResult<TSchema, TFields, TConfig>[]> {
	return new PgRelationalQuery(
		this.fullSchema,
		this.schema,
		this.tableNamesMap,
		this.table,
		this.tableConfig,
		this.dialect,
		this.session,
		config ? (config as DBQueryConfig<'many', true>) : {},
		'many',
	);
}

findFirst<TSelection extends Omit<DBQueryConfig<'many', true, TSchema, TFields>, 'limit'>>(
	config?: KnownKeysOnly<TSelection, Omit<DBQueryConfig<'many', true, TSchema, TFields>, 'limit'>>,
): PgRelationalQuery<BuildQueryResult<TSchema, TFields, TSelection> | undefined> {
	return new PgRelationalQuery(
		this.fullSchema,
		this.schema,
		this.tableNamesMap,
		this.table,
		this.tableConfig,
		this.dialect,
		this.session,
		config ? { ...(config as DBQueryConfig<'many', true> | undefined), limit: 1 } : { limit: 1 },
		'first',
	);
}
```
Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:32-62](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L32-L62)

> [!NOTE]
> In MySQL and SingleStore builders, relational query classes are parameterized by higher-kinded types (`PreparedQueryHKTBase`) to allow engines to generate dialect-specific prepared query handles.
> Sources: [drizzle-orm/src/mysql-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L23-L43), [drizzle-orm/src/singlestore-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/query.ts#L22-L41)

### Execution Lifecycle and Query Compilation

Relational query runner classes inherit from `QueryPromise` and implement query compilation, statement preparation, and row mapping. When `.execute()` or `.prepare()` is invoked, the runner calls an internal compilation method such as `_toSQL()` or `_getQuery()` which delegates to the dialect to build the underlying SQL abstract syntax tree (AST). For instance, PostgreSQL and Gel invoke `dialect.buildRelationalQueryWithoutPK`, whereas MySQL checks the connection mode (`planetscale`) to determine whether to build queries without lateral subqueries.

```typescript
private _getQuery() {
	const query = this.mode === 'planetscale'
		? this.dialect.buildRelationalQueryWithoutLateralSubqueries({
			fullSchema: this.fullSchema,
			schema: this.schema,
			tableNamesMap: this.tableNamesMap,
			table: this.table,
			tableConfig: this.tableConfig,
			queryConfig: this.config,
			tableAlias: this.tableConfig.tsName,
		})
		: this.dialect.buildRelationalQuery({
			fullSchema: this.fullSchema,
			schema: this.schema,
			tableNamesMap: this.tableNamesMap,
			table: this.table,
			tableConfig: this.tableConfig,
			queryConfig: this.config,
			tableAlias: this.tableConfig.tsName,
		});
	return query;
}
```
Sources: [drizzle-orm/src/mysql-core/query-builders/query.ts:114-135](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L114-L135)

Once compiled into SQL, the query is passed to the session via `prepareQuery` (or `prepareOneTimeQuery` in SQLite). The session callback processes raw database rows by executing `mapRelationalRow`, passing the schema table configuration, row selection mask, and column mapping utility. If the query execution mode is set to `'first'`, the result mapper extracts and returns the first element (`rows[0]`), otherwise returning the full array.

```typescript
	(rawRows, mapColumnValue) => {
		const rows = rawRows.map((row) =>
			mapRelationalRow(this.schema, this.tableConfig, row, query.selection, mapColumnValue)
		);
		if (this.mode === 'first') {
			return rows[0] as TResult;
		}
		return rows as TResult;
	},
);
```
Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:99-108](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L99-L108)

> [!WARNING]
> SQLite provides separate asynchronous (`SQLiteRelationalQuery`) and synchronous (`SQLiteSyncRelationalQuery`) classes where synchronous executions bypass promises and immediately invoke `.get()` or `.all()` through `executeRaw()`.
> Sources: [drizzle-orm/src/sqlite-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L98-L207)

## Lateral Joins and Subquery Compilation

### Dialect-Specific Subquery Strategies

Different database engines impose distinct constraints on how relational joins and nested subqueries are compiled. Drizzle accommodates these engine-specific capabilities by delegating query construction to dialect methods tailored to each backend's support for lateral subqueries, correlated references, and table primary key requirements.

```typescript
private _getQuery() {
	const query = this.mode === 'planetscale'
		? this.dialect.buildRelationalQueryWithoutLateralSubqueries({
			fullSchema: this.fullSchema,
			schema: this.schema,
			tableNamesMap: this.tableNamesMap,
			table: this.table,
			tableConfig: this.tableConfig,
			queryConfig: this.config,
			tableAlias: this.tableConfig.tsName,
		})
		: this.dialect.buildRelationalQuery({
			fullSchema: this.fullSchema,
			schema: this.schema,
			tableNamesMap: this.tableNamesMap,
			table: this.table,
			tableConfig: this.tableConfig,
			queryConfig: this.config,
			tableAlias: this.tableConfig.tsName,
		});
	return query;
}
```
Sources: [drizzle-orm/src/mysql-core/query-builders/query.ts:114-135](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L114-L135)

As shown in the MySQL relational query builder, the dialect branch inspection explicitly checks whether the execution target is running under PlanetScale mode. When `this.mode === 'planetscale'`, the query builder invokes `buildRelationalQueryWithoutLateralSubqueries` instead of standard `buildRelationalQuery`. This branching is required because serverless MySQL hosting environments often lack full lateral join support or enforce restrictions on correlated subquery constructs.

### Keyless and Specialized Compilation Methods

Beyond MySQL's conditional dispatch, PostgreSQL and Gel employ `buildRelationalQueryWithoutPK` to handle relational queries where primary key assumptions or table structures require specialized AST generation. 

```typescript
private _getQuery() {
	return this.dialect.buildRelationalQueryWithoutPK({
		fullSchema: this.fullSchema,
		schema: this.schema,
		tableNamesMap: this.tableNamesMap,
		table: this.table,
		tableConfig: this.tableConfig,
		queryConfig: this.config,
		tableAlias: this.tableConfig.tsName,
	});
}
```
Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:116-126](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L116-L126)

| Dialect / Core | Target Method | Branch Condition / Purpose |
| --- | --- | --- |
| PostgreSQL (`pg-core`) | `buildRelationalQueryWithoutPK` | Compiles relational queries without relying on explicit primary key column lookups in subquery correlations. |
| MySQL (`mysql-core`) | `buildRelationalQueryWithoutLateralSubqueries` | Executed when `mode === 'planetscale'` to avoid lateral join features unsupported by PlanetScale. |
| MySQL (`mysql-core`) | `buildRelationalQuery` | Standard MySQL path utilizing lateral subqueries and advanced join syntax. |
| SingleStore (`singlestore-core`) | `buildRelationalQuery` | Standard SingleStore path for relational query compilation. |
| SQLite (`sqlite-core`) | `buildRelationalQuery` | Standard SQLite path compiling relational selections into AST structures. |

Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:116-126](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L116-L126), [drizzle-orm/src/mysql-core/query-builders/query.ts:114-135](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L114-L135), [drizzle-orm/src/gel-core/query-builders/query.ts:116-126](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/query.ts#L116-L126), [drizzle-orm/src/singlestore-core/query-builders/query.ts:109-119](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/query.ts#L109-L119), [drizzle-orm/src/sqlite-core/query-builders/query.ts:129-138](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L129-L138)

> [!CAUTION]
> Attempting to execute standard lateral-join-based relational queries against a PlanetScale MySQL instance will fail at runtime unless the engine mode is explicitly configured to `'planetscale'`, which triggers `buildRelationalQueryWithoutLateralSubqueries`.
> Sources: [drizzle-orm/src/mysql-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L114-L135)

## Selection Masking and Dynamic Aliasing

### Selection Proxy Wrapping and Proxy Handlers

Selection masking and dynamic aliasing in Drizzle are governed by `SelectionProxyHandler`, which implements the JavaScript `ProxyHandler` interface across subqueries, record sets, and database views. When properties are accessed on a selection proxy, the handler intercepts symbol and string lookups to manage `_` metadata configurations, view configurations, and column selections.

```typescript
get(subquery: T, prop: string | symbol): any {
	if (prop === '_') {
		return {
			...subquery['_' as keyof typeof subquery],
			selectedFields: new Proxy(
				(subquery as Subquery)._.selectedFields,
				this as ProxyHandler<Record<string, unknown>>,
			),
		};
	}

	if (prop === ViewBaseConfig) {
		return {
			...subquery[ViewBaseConfig as keyof typeof subquery],
			selectedFields: new Proxy(
				(subquery as View)[ViewBaseConfig].selectedFields,
				this as ProxyHandler<Record<string, unknown>>,
			),
		};
	}
```
Sources: [drizzle-orm/src/selection-proxy.ts:47-66](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/selection-proxy.ts#L47-L66)

The proxy handler evaluates accessed properties to determine whether they represent stored columns, raw SQL expressions, aliased SQL chunks, or nested objects. When a column is encountered and an alias is configured, the handler wraps the column and its underlying table within `ColumnAliasProxyHandler` and `TableAliasProxyHandler` respectively.

```typescript
if (is(value, Column)) {
	if (this.config.alias) {
		return new Proxy(
			value,
			new ColumnAliasProxyHandler(
				new Proxy(
					value.table,
					new TableAliasProxyHandler(this.config.alias, this.config.replaceOriginalName ?? false),
				),
			),
		);
	}
	return value;
}
```
Sources: [drizzle-orm/src/selection-proxy.ts:100-113](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/selection-proxy.ts#L100-L113)

### Column Projection Scoping and SQL Behavior

The configuration options accepted by `SelectionProxyHandler` control how raw SQL expressions and aliased fields behave during query building and view generation.

| Configuration Key | Type | Default / Options | Purpose |
| --- | --- | --- | --- |
| `alias` | `string` | `undefined` | Table alias applied to intercepted columns during proxy wrapping. |
| `sqlAliasedBehavior` | `'sql' \| 'alias'` | Required | Determines whether `SQL.Aliased` returns the underlying expression (`'sql'`) or the field alias (`'alias'`). |
| `sqlBehavior` | `'sql' \| 'error'` | Required | Determines whether unaliased raw `SQL` expressions return the expression or throw a type/runtime error. |
| `replaceOriginalName` | `boolean` | `false` | Replaces the original column name with its alias, primarily used when creating database views. |

Sources: [drizzle-orm/src/selection-proxy.ts:13-41](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/selection-proxy.ts#L13-L41)

> [!WARNING]
> Accessing a raw SQL field from a subquery that lacks an explicit alias will throw an error at runtime and evaluate to a Drizzle type-level error when `sqlBehavior` is set to `'error'`.
> Sources: [drizzle-orm/src/selection-proxy.ts:90-98](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/selection-proxy.ts#L90-L98)

> [!NOTE]
> `SQL.Aliased` instances that originated inside a prior subquery carry `isSelectionField: true`, preventing the proxy handler from mistakenly returning raw underlying SQL expressions when `sqlAliasedBehavior` is set to `'sql'`.
> Sources: [drizzle-orm/src/selection-proxy.ts:79-88](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/selection-proxy.ts#L79-L88)

## Result Hydration and Row Mapping

### Relational Row Transformation and Mapping Lifecycles

After database sessions execute compiled relational queries, raw tabular rows returned by drivers require structural reshaping into nested object graphs matching the user's schema and selection configuration. Across PostgreSQL, MySQL, SQLite, SingleStore, and Gel dialects, this post-query phase is driven by `mapRelationalRow`, which processes raw row data and column value mappers provided by underlying database drivers.

```typescript
(rawRows, mapColumnValue) => {
	const rows = rawRows.map((row) =>
		mapRelationalRow(this.schema, this.tableConfig, row, query.selection, mapColumnValue)
	);
	if (this.mode === 'first') {
		return rows[0] as TResult;
	}
	return rows as TResult;
},
```
Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:99-107](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L99-L107)

For asynchronous and synchronous session configurations, the execution pipeline wraps the raw rows mapper inside prepared query definitions. Depending on whether the query mode is configured as `'many'` or `'first'`, the result handler returns either an array of fully hydrated entities or extracts the initial index element.

```typescript
(rawRows, mapColumnValue) => {
	const rows = rawRows.map((row) =>
		mapRelationalRow(this.schema, this.tableConfig, row, query.selection, mapColumnValue)
	);
	if (this.mode === 'first') {
		return rows[0] as TResult;
	}
	return rows as TResult;
},
```
Sources: [drizzle-orm/src/sqlite-core/query-builders/query.ts:152-160](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L152-L160)

### Execution and Preparation Modalities

Different query runner implementations manage row mapping through dialect-specific prepared statements and query promises.

| Dialect Runner Class | Method / Hook | Mode Parameter | Return Processing |
| --- | --- | --- | --- |
| `PgRelationalQuery` | `_prepare` | `'many'` \| `'first'` | Maps raw rows with `mapRelationalRow`, slicing index `0` for `'first'`. |
| `MySqlRelationalQuery` | `prepare` | `'many'` \| `'first'` | Iterates over `rawRows` via `mapRelationalRow` without column value mappers. |
| `SQLiteRelationalQuery` | `_prepare` | `'many'` \| `'first'` | Dispatches to session `prepareQuery` or `prepareOneTimeQuery` with driver mappers. |
| `GelRelationalQuery` | `_prepare` | `'many'` \| `'first'` | Executes via Gel session wrapper, applying active telemetry span tracing. |
| `SingleStoreRelationalQuery` | `prepare` | `'many'` \| `'first'` | Processes result sets via SingleStore session query preparation layer. |

Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:90-108](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L90-L108), [drizzle-orm/src/mysql-core/query-builders/query.ts:99-111](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/query-builders/query.ts#L99-L111), [drizzle-orm/src/sqlite-core/query-builders/query.ts:142-161](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L142-L161), [drizzle-orm/src/gel-core/query-builders/query.ts:90-108](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/query.ts#L90-L108), [drizzle-orm/src/singlestore-core/query-builders/query.ts:94-106](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/query.ts#L94-L106)

> [!NOTE]
> PostgreSQL, Gel, and SQLite sessions pass a secondary `mapColumnValue` callback into `mapRelationalRow`, enabling custom type decoding per column during row deserialization.
> Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:99-102](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L99-L102), [drizzle-orm/src/gel-core/query-builders/query.ts:99-102](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/query.ts#L99-L102), [drizzle-orm/src/sqlite-core/query-builders/query.ts:152-155](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L152-L155)

> [!WARNING]
> Selecting `findFirst` on any relational query builder forces the row transformation pipeline to truncate the hydrated array output, returning `undefined` if no records match the criteria.
> Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:48-62](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L48-L62), [drizzle-orm/src/sqlite-core/query-builders/query.ts:69-95](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/query-builders/query.ts#L69-L95)

## Related

- [Table and Column Models](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/core-architecture/table-and-column-models)
- [Query Builder Foundation](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/core-architecture/query-builder-foundation)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/llms.txt) for all pages in this wiki.
