/**
* Create a factory for building ALTER statements.
*
* Provides a unified entry point for all ALTER operations:
* - alter()->table() - ALTER TABLE
* - alter()->index() - ALTER INDEX
* - alter()->view() - ALTER VIEW
* - alter()->materializedView() - ALTER MATERIALIZED VIEW
* - alter()->sequence() - ALTER SEQUENCE
* - alter()->schema() - ALTER SCHEMA
* - alter()->role() - ALTER ROLE
* - alter()->function() - ALTER FUNCTION
* - alter()->procedure() - ALTER PROCEDURE
* - alter()->trigger() - ALTER TRIGGER
* - alter()->extension() - ALTER EXTENSION
* - alter()->enumType() - ALTER TYPE (enum)
* - alter()->domain() - ALTER DOMAIN
*
* Rename operations are also under alter():
* - alter()->index('old')->renameTo('new')
* - alter()->view('old')->renameTo('new')
* - alter()->schema('old')->renameTo('new')
* - alter()->role('old')->renameTo('new')
* - alter()->trigger('old')->on('table')->renameTo('new')
*
* Example: alter()->table('users')->addColumn(col_def('email', data_type_text()))
* Example: alter()->sequence('user_id_seq')->restart(1000)
*/
alter() : AlterFactory DSL References
DSL stands for Domain Specific Language. In the case of Flow, the DSL is used to define simple functions that can be used to transform data. Most of those functions are initializing a new instance of a class under the hood since Flow is fully object-oriented. Please look at the examples below to get a better understanding of how to use the DSL functions.
SCHEMA
/**
* Create a factory for building CREATE statements.
*
* Provides a unified entry point for all CREATE operations:
* - create()->table() - CREATE TABLE
* - create()->tableAs() - CREATE TABLE AS
* - create()->index() - CREATE INDEX
* - create()->view() - CREATE VIEW
* - create()->materializedView() - CREATE MATERIALIZED VIEW
* - create()->sequence() - CREATE SEQUENCE
* - create()->schema() - CREATE SCHEMA
* - create()->role() - CREATE ROLE
* - create()->function() - CREATE FUNCTION
* - create()->procedure() - CREATE PROCEDURE
* - create()->trigger() - CREATE TRIGGER
* - create()->rule() - CREATE RULE
* - create()->extension() - CREATE EXTENSION
* - create()->compositeType() - CREATE TYPE (composite)
* - create()->enumType() - CREATE TYPE (enum)
* - create()->rangeType() - CREATE TYPE (range)
* - create()->domain() - CREATE DOMAIN
*
* Example: create()->table('users')->columns(col_def('id', data_type_serial()))
* Example: create()->index('idx_email')->on('users')->columns('email')
*/
create() : CreateFactory /**
* Create a factory for building DROP statements.
*
* Provides a unified entry point for all DROP operations:
* - drop()->table() - DROP TABLE
* - drop()->index() - DROP INDEX
* - drop()->view() - DROP VIEW
* - drop()->materializedView() - DROP MATERIALIZED VIEW
* - drop()->sequence() - DROP SEQUENCE
* - drop()->schema() - DROP SCHEMA
* - drop()->role() - DROP ROLE
* - drop()->function() - DROP FUNCTION
* - drop()->procedure() - DROP PROCEDURE
* - drop()->trigger() - DROP TRIGGER
* - drop()->rule() - DROP RULE
* - drop()->extension() - DROP EXTENSION
* - drop()->type() - DROP TYPE
* - drop()->domain() - DROP DOMAIN
* - drop()->owned() - DROP OWNED
*
* Example: drop()->table('users', 'orders')->ifExists()->cascade()
* Example: drop()->index('idx_email')->ifExists()
*/
drop() : DropFactory /**
* Create an index column specification.
*
* Use chainable methods: ->asc(), ->desc(), ->nullsFirst(), ->nullsLast(), ->opclass(), ->collate()
*
* Example: index_col('email')->desc()->nullsLast()
*
* @param string $name The column name
*/
index_col(string $name) : IndexColumn /**
* Create an index column specification from an expression.
*
* Use chainable methods: ->asc(), ->desc(), ->nullsFirst(), ->nullsLast(), ->opclass(), ->collate()
*
* Example: index_expr(fn_call('lower', col('email')))->desc()
*
* @param Expression $expression The expression to index
*/
index_expr(Expression $expression) : IndexColumn /**
* Create a REFRESH MATERIALIZED VIEW builder.
*
* Example: refresh_materialized_view('user_stats')
* Produces: REFRESH MATERIALIZED VIEW user_stats
*
* Example: refresh_materialized_view('user_stats')->concurrently()->withData()
* Produces: REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats WITH DATA
*
* @param string $name View name (may include schema as "schema.view")
* @param null|string $schema Schema name (optional, overrides parsed schema)
*/
refresh_materialized_view(string $name, ?string $schema) : RefreshMatViewOptionsStep /**
* Start building a REINDEX DATABASE statement.
*
* Use chainable methods: ->concurrently(), ->verbose(), ->tablespace()
*
* Example: reindex_database('mydb')->concurrently()
*
* @param string $name The database name
*/
reindex_database(string $name) : ReindexFinalStep /**
* Start building a REINDEX INDEX statement.
*
* Use chainable methods: ->concurrently(), ->verbose(), ->tablespace()
*
* Example: reindex_index('idx_users_email')->concurrently()
*
* @param string $name The index name (may include schema: schema.index)
*/
reindex_index(string $name) : ReindexFinalStep /**
* Start building a REINDEX SCHEMA statement.
*
* Use chainable methods: ->concurrently(), ->verbose(), ->tablespace()
*
* Example: reindex_schema('public')->concurrently()
*
* @param string $name The schema name
*/
reindex_schema(string $name) : ReindexFinalStep /**
* Start building a REINDEX TABLE statement.
*
* Use chainable methods: ->concurrently(), ->verbose(), ->tablespace()
*
* Example: reindex_table('users')->concurrently()
*
* @param string $name The table name (may include schema: schema.table)
*/
reindex_table(string $name) : ReindexFinalStep HELPER
/**
* Create an aggregate function call (COUNT, SUM, AVG, etc.).
*
* @param string $name Aggregate function name
* @param list<Expression> $args Function arguments
* @param bool $distinct Use DISTINCT modifier
*/
agg(string $name, array $args, bool $distinct) : AggregateCall /**
* Create AVG aggregate.
*/
agg_avg(Expression $expr, bool $distinct) : AggregateCall /**
* Create COUNT(*) aggregate.
*/
agg_count(?Expression $expr, bool $distinct) : AggregateCall /**
* Create MAX aggregate.
*/
agg_max(Expression $expr) : AggregateCall /**
* Create MIN aggregate.
*/
agg_min(Expression $expr) : AggregateCall /**
* Create SUM aggregate.
*/
agg_sum(Expression $expr, bool $distinct) : AggregateCall /**
* Create an ALL condition.
*/
all_sub_select(Expression $left, ComparisonOperator $operator, SelectFinalStep $subquery) : All /**
* Create an ANALYZE builder.
*
* Example: analyze()->table('users')
* Produces: ANALYZE users
*/
analyze() : AnalyzeFinalStep /**
* Create an ANY condition.
*/
any_sub_select(Expression $left, ComparisonOperator $operator, SelectFinalStep $subquery) : Any /**
* Create an array is contained by condition (<@).
*
* Example: array_contained_by(col('tags'), raw_expr("ARRAY['sale', 'featured', 'new']"))
* Produces: tags <@ ARRAY['sale', 'featured', 'new']
*/
array_contained_by(Expression $left, Expression $right) : OperatorCondition /**
* Create an array contains condition (@>).
*
* Example: array_contains(col('tags'), raw_expr("ARRAY['sale']"))
* Produces: tags @> ARRAY['sale']
*/
array_contains(Expression $left, Expression $right) : OperatorCondition /**
* Create an array expression.
*
* @param list<Expression> $elements Array elements
*/
array_expr(array $elements) : ArrayExpression /**
* Create an array overlap condition (&&).
*
* Example: array_overlap(col('tags'), raw_expr("ARRAY['sale', 'featured']"))
* Produces: tags && ARRAY['sale', 'featured']
*/
array_overlap(Expression $left, Expression $right) : OperatorCondition /**
* Create an ORDER BY item with ASC direction.
*/
asc(Expression $expr, NullsPosition $nulls) : OrderByItem /**
* Create a BEGIN transaction builder.
*
* Example: begin()->isolationLevel(IsolationLevel::SERIALIZABLE)->readOnly()
* Produces: BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY
*/
begin() : BeginOptionsStep /**
* Create a BETWEEN condition.
*/
between(Expression $expr, Expression $low, Expression $high, bool $not) : Between /**
* Create a binary expression (left op right).
*/
binary_expr(Expression $left, string $operator, Expression $right) : BinaryExpression /**
* Creates a CALL statement builder for invoking a procedure.
*
* Example: call('update_stats')->with(123)
* Produces: CALL update_stats(123)
*
* Example: call('process_data')->with('test', 42, true)
* Produces: CALL process_data('test', 42, true)
*
* @param string $procedure The name of the procedure to call
*
* @return CallFinalStep Builder for call statement options
*/
call(string $procedure) : CallFinalStep /**
* Create a CASE expression.
*
* @param non-empty-list<WhenClause> $whenClauses WHEN clauses
* @param null|Expression $elseResult ELSE result (optional)
* @param null|Expression $operand CASE operand for simple CASE (optional)
*/
case_when(array $whenClauses, ?Expression $elseResult, ?Expression $operand) : CaseExpression /**
* Create a type cast expression.
*
* @param Expression $expr Expression to cast
* @param DataType $dataType Target data type (use data_type_* functions)
*/
cast(Expression $expr, DataType $dataType) : TypeCast /**
* Create a CHECK constraint.
*
* @param string $expression SQL expression that must evaluate to true
*/
check_constraint(string $expression) : CheckConstraint /**
* Create a CLUSTER builder.
*
* Example: cluster()->table('users')->using('idx_users_pkey')
* Produces: CLUSTER users USING idx_users_pkey
*/
cluster() : ClusterFinalStep /**
* Create a COALESCE expression.
*
* @param Expression ...$expressions Expressions to coalesce
*/
coalesce(Expression $expressions) : Coalesce /**
* Create a column reference expression.
*
* Can be used in two modes:
* - Parse mode: col('users.id') or col('schema.table.column') - parses dot-separated string
* - Explicit mode: col('id', 'users') or col('id', 'users', 'schema') - separate arguments
*
* When $table or $schema is provided, $column must be a plain column name (no dots).
*
* @param string $column Column name, or dot-separated path like "table.column" or "schema.table.column"
* @param null|string $table Table name (optional, triggers explicit mode)
* @param null|string $schema Schema name (optional, requires $table)
*
* @throws InvalidExpressionException when $schema is provided without $table, or when $column contains dots in explicit mode
*/
col(string $column, ?string $table, ?string $schema) : Column /**
* Create a column definition for CREATE TABLE.
*
* @param string $name Column name
* @param DataType $type Column data type
*/
column(string $name, DataType $type) : ColumnDefinition /**
* Create a COMMENT ON builder.
*
* Example: comment(CommentTarget::TABLE, 'users')->is('User accounts table')
* Produces: COMMENT ON TABLE users IS 'User accounts table'
*
* @param CommentTarget $target Target type (TABLE, COLUMN, INDEX, etc.)
* @param string $name Target name (use 'table.column' for COLUMN targets)
*/
comment(CommentTarget $target, string $name) : CommentFinalStep /**
* Create a COMMIT transaction builder.
*
* Example: commit()->andChain()
* Produces: COMMIT AND CHAIN
*/
commit() : CommitOptionsStep /**
* Create a COMMIT PREPARED builder.
*
* Example: commit_prepared('my_transaction')
* Produces: COMMIT PREPARED 'my_transaction'
*/
commit_prepared(string $transactionId) : PreparedTransactionFinalStep /**
* Combine conditions with AND.
*
* @param Condition ...$conditions Conditions to combine
*/
cond_and(Condition $conditions) : AndCondition /**
* Create a FALSE condition for WHERE clauses.
*
* Useful when you need a condition that always evaluates to false,
* typically for testing or to return an empty result set.
*
* Example: select(literal(1))->where(cond_false()) // SELECT 1 WHERE false
*/
cond_false() : RawCondition /**
* Negate a condition with NOT.
*/
cond_not(Condition $condition) : NotCondition /**
* Combine conditions with OR.
*
* @param Condition ...$conditions Conditions to combine
*/
cond_or(Condition $conditions) : OrCondition /**
* Create a TRUE condition for WHERE clauses.
*
* Useful when you need a condition that always evaluates to true.
*
* Example: select(literal(1))->where(cond_true()) // SELECT 1 WHERE true
*/
cond_true() : RawCondition /**
* Create a conflict target for ON CONFLICT (columns).
*
* @param list<string> $columns Columns that define uniqueness
*/
conflict_columns(array $columns) : ConflictTarget /**
* Create a conflict target for ON CONFLICT ON CONSTRAINT.
*/
conflict_constraint(string $name) : ConflictTarget /**
* Create a new COPY query builder for data import/export.
*
* Usage:
* copy()->from('users')->file('/tmp/users.csv')->format(CopyFormat::CSV)
* copy()->to('users')->file('/tmp/users.csv')->format(CopyFormat::CSV)
* copy()->toQuery(select(...))->file('/tmp/data.csv')
*/
copy() : CopyFactory /**
* Create a CTE (Common Table Expression).
*
* @param string $name CTE name
* @param SelectFinalStep $query CTE query
* @param array<string> $columnNames Column aliases (optional)
* @param CTEMaterialization $materialization Materialization hint
*/
cte(string $name, SelectFinalStep $query, array $columnNames, CTEMaterialization $materialization, bool $recursive) : CTE /**
* SQL standard CURRENT_DATE function.
*
* Returns the current date (at the start of the transaction).
* Useful as a column default value or in SELECT queries.
*
* Example: column('birth_date', data_type_date())->default(current_date())
* Example: select()->select(current_date()->as('today'))
*/
current_date() : SQLValueFunctionExpression /**
* SQL standard CURRENT_TIME function.
*
* Returns the current time (at the start of the transaction).
* Useful as a column default value or in SELECT queries.
*
* Example: column('start_time', data_type_time())->default(current_time())
* Example: select()->select(current_time()->as('now_time'))
*/
current_time() : SQLValueFunctionExpression /**
* SQL standard CURRENT_TIMESTAMP function.
*
* Returns the current date and time (at the start of the transaction).
* Useful as a column default value or in SELECT queries.
*
* Example: column('created_at', data_type_timestamp())->default(current_timestamp())
* Example: select()->select(current_timestamp()->as('now'))
*/
current_timestamp() : SQLValueFunctionExpression /**
* Create an array data type from an element type.
*/
data_type_array(DataType $elementType) : DataType /**
* Create a bigint data type (PostgreSQL int8).
*/
data_type_bigint() : DataType /**
* Create a bigserial data type.
*/
data_type_bigserial() : DataType /**
* Create a boolean data type.
*/
data_type_boolean() : DataType /**
* Create a bytea data type.
*/
data_type_bytea() : DataType /**
* Create a char data type with length constraint.
*/
data_type_char(int $length) : DataType /**
* Create a cidr data type.
*/
data_type_cidr() : DataType /**
* Create a custom data type.
*
* @param string $typeName Type name
* @param null|string $schema Optional schema name
*/
data_type_custom(string $typeName, ?string $schema) : DataType /**
* Create a date data type.
*/
data_type_date() : DataType /**
* Create a decimal data type with optional precision and scale.
*/
data_type_decimal(?int $precision, ?int $scale) : DataType /**
* Create a double precision data type (PostgreSQL float8).
*/
data_type_double_precision() : DataType /**
* Create an inet data type.
*/
data_type_inet() : DataType /**
* Create an integer data type (PostgreSQL int4).
*/
data_type_integer() : DataType /**
* Create an interval data type.
*/
data_type_interval() : DataType /**
* Create a JSON data type.
*/
data_type_json() : DataType /**
* Create a JSONB data type.
*/
data_type_jsonb() : DataType /**
* Create a macaddr data type.
*/
data_type_macaddr() : DataType /**
* Create a numeric data type with optional precision and scale.
*/
data_type_numeric(?int $precision, ?int $scale) : DataType /**
* Create a real data type (PostgreSQL float4).
*/
data_type_real() : DataType /**
* Create a serial data type.
*/
data_type_serial() : DataType /**
* Create a smallint data type (PostgreSQL int2).
*/
data_type_smallint() : DataType /**
* Create a smallserial data type.
*/
data_type_smallserial() : DataType /**
* Create a text data type.
*/
data_type_text() : DataType /**
* Create a time data type with optional precision.
*/
data_type_time(?int $precision) : DataType /**
* Create a timestamp data type with optional precision.
*/
data_type_timestamp(?int $precision) : DataType /**
* Create a timestamp with time zone data type with optional precision.
*/
data_type_timestamptz(?int $precision) : DataType /**
* Create a UUID data type.
*/
data_type_uuid() : DataType /**
* Create a varchar data type with length constraint.
*/
data_type_varchar(int $length) : DataType /**
* Create a new DELETE query builder.
*/
delete() : DeleteFromStep /**
* Create a derived table (subquery in FROM clause).
*/
derived(SelectFinalStep $query, string $alias) : DerivedTable /**
* Create an ORDER BY item with DESC direction.
*/
desc(Expression $expr, NullsPosition $nulls) : OrderByItem /**
* Create a DISCARD builder.
*
* Example: discard(DiscardType::ALL)
* Produces: DISCARD ALL
*
* @param DiscardType $type Type of resources to discard (ALL, PLANS, SEQUENCES, TEMP)
*/
discard(DiscardType $type) : DiscardFinalStep /**
* Creates a DO statement builder for executing an anonymous code block.
*
* Example: do_block('BEGIN RAISE NOTICE $$Hello World$$; END;')
* Produces: DO $$ BEGIN RAISE NOTICE $$Hello World$$; END; $$ LANGUAGE plpgsql
*
* Example: do_block('SELECT 1')->language('sql')
* Produces: DO $$ SELECT 1 $$ LANGUAGE sql
*
* @param string $code The anonymous code block to execute
*
* @return DoFinalStep Builder for DO statement options
*/
do_block(string $code) : DoFinalStep /**
* Create a DROP OWNED builder.
*
* Example: drop_owned('role1')
* Produces: DROP OWNED BY role1
*
* Example: drop_owned('role1', 'role2')->cascade()
* Produces: DROP OWNED BY role1, role2 CASCADE
*
* @param string ...$roles The roles whose owned objects should be dropped
*
* @return DropOwnedFinalStep Builder for drop owned options
*/
drop_owned(string $roles) : DropOwnedFinalStep /**
* Create an equality comparison (column = value).
*/
eq(Expression $left, Expression $right) : Comparison /**
* Create an EXISTS condition.
*/
exists(SelectFinalStep $subquery) : Exists /**
* Create an EXPLAIN builder for a query.
*
* Example: explain(select()->from('users'))
* Produces: EXPLAIN SELECT * FROM users
*
* @param DeleteBuilder|InsertBuilder|SelectFinalStep|UpdateBuilder $query Query to explain
*/
explain(SelectFinalStep|InsertBuilder|UpdateBuilder|DeleteBuilder $query) : ExplainFinalStep /**
* Create a FOREIGN KEY constraint.
*
* @param list<string> $columns Local columns
* @param string $referenceTable Referenced table
* @param list<string> $referenceColumns Referenced columns (defaults to same as $columns if empty)
*/
foreign_key(array $columns, string $referenceTable, array $referenceColumns) : ForeignKeyConstraint /**
* Create a FOR SHARE locking clause.
*
* @param list<string> $tables Tables to lock (empty for all)
*/
for_share(array $tables) : LockingClause /**
* Create a FOR UPDATE locking clause.
*
* @param list<string> $tables Tables to lock (empty for all)
*/
for_update(array $tables) : LockingClause /**
* Create a frame bound for CURRENT ROW.
*/
frame_current_row() : FrameBound /**
* Create a frame bound for N FOLLOWING.
*/
frame_following(Expression $offset) : FrameBound /**
* Create a frame bound for N PRECEDING.
*/
frame_preceding(Expression $offset) : FrameBound /**
* Create a frame bound for UNBOUNDED FOLLOWING.
*/
frame_unbounded_following() : FrameBound /**
* Create a frame bound for UNBOUNDED PRECEDING.
*/
frame_unbounded_preceding() : FrameBound /**
* Create a function call expression.
*
* @param string $name Function name (can include schema like "pg_catalog.now")
* @param list<Expression> $args Function arguments
*/
func(string $name, array $args) : FunctionCall /**
* Creates a new function argument for use in function/procedure definitions.
*
* Example: func_arg(data_type_integer())
* Example: func_arg(data_type_text())->named('username')
* Example: func_arg(data_type_integer())->named('count')->default('0')
* Example: func_arg(data_type_text())->out()
*
* @param DataType $type The PostgreSQL data type for the argument
*
* @return FunctionArgument Builder for function argument options
*/
func_arg(DataType $type) : FunctionArgument /**
* Create a GRANT privileges builder.
*
* Example: grant(TablePrivilege::SELECT)->onTable('users')->to('app_user')
* Produces: GRANT SELECT ON users TO app_user
*
* Example: grant(TablePrivilege::ALL)->onAllTablesInSchema('public')->to('admin')
* Produces: GRANT ALL ON ALL TABLES IN SCHEMA public TO admin
*
* @param string|TablePrivilege ...$privileges The privileges to grant
*
* @return GrantOnStep Builder for grant options
*/
grant(TablePrivilege|string $privileges) : GrantOnStep /**
* Create a GRANT role builder.
*
* Example: grant_role('admin')->to('user1')
* Produces: GRANT admin TO user1
*
* Example: grant_role('admin', 'developer')->to('user1')->withAdminOption()
* Produces: GRANT admin, developer TO user1 WITH ADMIN OPTION
*
* @param string ...$roles The roles to grant
*
* @return GrantRoleToStep Builder for grant role options
*/
grant_role(string $roles) : GrantRoleToStep /**
* Create a GREATEST expression.
*
* @param Expression ...$expressions Expressions to compare
*/
greatest(Expression $expressions) : Greatest /**
* Create a greater-than comparison (column > value).
*/
gt(Expression $left, Expression $right) : Comparison /**
* Create a greater-than-or-equal comparison (column >= value).
*/
gte(Expression $left, Expression $right) : Comparison /**
* Get the BRIN index method.
*/
index_method_brin() : IndexMethod /**
* Get the BTREE index method.
*/
index_method_btree() : IndexMethod /**
* Get the GIN index method.
*/
index_method_gin() : IndexMethod /**
* Get the GIST index method.
*/
index_method_gist() : IndexMethod /**
* Get the HASH index method.
*/
index_method_hash() : IndexMethod /**
* Get the SPGIST index method.
*/
index_method_spgist() : IndexMethod /**
* Create a new INSERT query builder.
*/
insert() : InsertIntoStep /**
* Create an IS DISTINCT FROM condition.
*/
is_distinct_from(Expression $left, Expression $right, bool $not) : IsDistinctFrom /**
* Create an IN condition.
*
* @param Expression $expr Expression to check
* @param list<Expression> $values List of values
*/
is_in(Expression $expr, array $values) : In /**
* Create an IS NULL condition.
*/
is_null(Expression $expr, bool $not) : IsNull /**
* Create a JSONB is contained by condition (<@).
*
* Example: json_contained_by(col('metadata'), literal_json('{"category": "electronics", "price": 100}'))
* Produces: metadata <@ '{"category": "electronics", "price": 100}'
*/
json_contained_by(Expression $left, Expression $right) : OperatorCondition /**
* Create a JSONB contains condition (@>).
*
* Example: json_contains(col('metadata'), literal_json('{"category": "electronics"}'))
* Produces: metadata @> '{"category": "electronics"}'
*/
json_contains(Expression $left, Expression $right) : OperatorCondition /**
* Create a JSONB key exists condition (?).
*
* Example: json_exists(col('metadata'), literal_string('category'))
* Produces: metadata ? 'category'
*/
json_exists(Expression $expr, Expression $key) : OperatorCondition /**
* Create a JSONB all keys exist condition (?&).
*
* Example: json_exists_all(col('metadata'), raw_expr("array['category', 'name']"))
* Produces: metadata ?& array['category', 'name']
*/
json_exists_all(Expression $expr, Expression $keys) : OperatorCondition /**
* Create a JSONB any key exists condition (?|).
*
* Example: json_exists_any(col('metadata'), raw_expr("array['category', 'name']"))
* Produces: metadata ?| array['category', 'name']
*/
json_exists_any(Expression $expr, Expression $keys) : OperatorCondition /**
* Create a JSON field access expression (->).
* Returns JSON.
*
* Example: json_get(col('metadata'), literal_string('category'))
* Produces: metadata -> 'category'
*/
json_get(Expression $expr, Expression $key) : BinaryExpression /**
* Create a JSON field access expression (->>).
* Returns text.
*
* Example: json_get_text(col('metadata'), literal_string('name'))
* Produces: metadata ->> 'name'
*/
json_get_text(Expression $expr, Expression $key) : BinaryExpression /**
* Create a JSON path access expression (#>).
* Returns JSON.
*
* Example: json_path(col('metadata'), literal_string('{category,name}'))
* Produces: metadata #> '{category,name}'
*/
json_path(Expression $expr, Expression $path) : BinaryExpression /**
* Create a JSON path access expression (#>>).
* Returns text.
*
* Example: json_path_text(col('metadata'), literal_string('{category,name}'))
* Produces: metadata #>> '{category,name}'
*/
json_path_text(Expression $expr, Expression $path) : BinaryExpression /**
* Create a LATERAL subquery.
*
* @param TableReference $reference The subquery or table function reference
*/
lateral(TableReference $reference) : Lateral /**
* Create a LEAST expression.
*
* @param Expression ...$expressions Expressions to compare
*/
least(Expression $expressions) : Least /**
* Create a LIKE condition.
*/
like(Expression $expr, Expression $pattern, bool $caseInsensitive) : Like /**
* Create a literal value for use in queries.
*
* Automatically detects the type and creates the appropriate literal:
* - literal('hello') creates a string literal
* - literal(42) creates an integer literal
* - literal(3.14) creates a float literal
* - literal(true) creates a boolean literal
* - literal(null) creates a NULL literal
*/
literal(string|int|float|bool|null $value) : Literal /**
* Create a locking clause (FOR UPDATE, FOR SHARE, etc.).
*
* @param LockStrength $strength Lock strength
* @param list<string> $tables Tables to lock (empty for all)
* @param LockWaitPolicy $waitPolicy Wait policy
*/
lock_for(LockStrength $strength, array $tables, LockWaitPolicy $waitPolicy) : LockingClause /**
* Create a LOCK TABLE builder.
*
* Example: lock_table('users', 'orders')->accessExclusive()
* Produces: LOCK TABLE users, orders IN ACCESS EXCLUSIVE MODE
*/
lock_table(string $tables) : LockFinalStep /**
* Create a less-than comparison (column < value).
*/
lt(Expression $left, Expression $right) : Comparison /**
* Create a less-than-or-equal comparison (column <= value).
*/
lte(Expression $left, Expression $right) : Comparison /**
* Create a new MERGE query builder.
*
* @param string $table Target table name
* @param null|string $alias Optional table alias
*/
merge(string $table, ?string $alias) : MergeUsingStep /**
* Create a not-equal comparison (column != value).
*/
neq(Expression $left, Expression $right) : Comparison /**
* Create a POSIX regex not match condition (!~*).
* Case-insensitive.
*
* Example: not_regex_imatch(col('email'), literal_string('.*@spam\\.com'))
*
* Produces: email !~* '.*@spam\.com'
*/
not_regex_imatch(Expression $expr, Expression $pattern) : OperatorCondition /**
* Create a POSIX regex not match condition (!~).
* Case-sensitive.
*
* Example: not_regex_match(col('email'), literal_string('.*@spam\\.com'))
*
* Produces: email !~ '.*@spam\.com'
*/
not_regex_match(Expression $expr, Expression $pattern) : OperatorCondition /**
* Create a NULLIF expression.
*/
nullif(Expression $expr1, Expression $expr2) : NullIf /**
* Create an ON CONFLICT DO NOTHING clause.
*/
on_conflict_nothing(?ConflictTarget $target) : OnConflictClause /**
* Create an ON CONFLICT DO UPDATE clause.
*
* @param ConflictTarget $target Conflict target (columns or constraint)
* @param array<string, Expression> $updates Column updates
*/
on_conflict_update(ConflictTarget $target, array $updates) : OnConflictClause /**
* Create an ORDER BY item.
*/
order_by(Expression $expr, SortDirection $direction, NullsPosition $nulls) : OrderByItem /**
* Create a positional parameter ($1, $2, etc.).
*/
param(int $position) : Parameter /**
* Create a PostgreSQL client using ext-pgsql.
*
* The client connects immediately and is ready to execute queries.
* For object mapping, provide a RowMapper (use pgsql_mapper() for the default).
*
* @param Client\ConnectionParameters $params Connection parameters
* @param null|ValueConverters $valueConverters Custom type converters (optional)
* @param null|Client\RowMapper $mapper Row mapper for object hydration (optional)
*
* @throws ConnectionException If connection fails
*
* @example
* // Basic client
* $client = pgsql_client(pgsql_connection('host=localhost dbname=mydb'));
*
* // With object mapping
* $client = pgsql_client(
* pgsql_connection('host=localhost dbname=mydb'),
* mapper: pgsql_mapper(),
* );
*/
pgsql_client(ConnectionParameters $params, ?ValueConverters $valueConverters, ?RowMapper $mapper) : Client /**
* Create connection parameters from a connection string.
*
* Accepts libpq-style connection strings:
* - Key-value format: "host=localhost port=5432 dbname=mydb user=myuser password=secret"
* - URI format: "postgresql://user:password@localhost:5432/dbname"
*
* @example
* $params = pgsql_connection('host=localhost dbname=mydb');
* $params = pgsql_connection('postgresql://user:pass@localhost/mydb');
*/
pgsql_connection(string $connectionString) : ConnectionParameters /**
* Create connection parameters from a DSN string.
*
* Parses standard PostgreSQL DSN format commonly used in environment variables
* (e.g., DATABASE_URL). Supports postgres://, postgresql://, and pgsql:// schemes.
*
* @param string $dsn DSN string in format: postgres://user:password@host:port/database?options
*
* @throws Client\DsnParserException If the DSN cannot be parsed
*
* @example
* $params = pgsql_connection_dsn('postgres://myuser:secret@localhost:5432/mydb');
* $params = pgsql_connection_dsn('postgresql://user:[email protected]/app?sslmode=require');
* $params = pgsql_connection_dsn('pgsql://user:pass@localhost/mydb'); // Symfony/Doctrine format
* $params = pgsql_connection_dsn(getenv('DATABASE_URL'));
*/
pgsql_connection_dsn(string $dsn) : ConnectionParameters /**
* Create connection parameters from individual values.
*
* Allows specifying connection parameters individually for better type safety
* and IDE support.
*
* @param string $database Database name (required)
* @param string $host Hostname (default: localhost)
* @param int $port Port number (default: 5432)
* @param null|string $user Username (optional)
* @param null|string $password Password (optional)
* @param array<string, string> $options Additional libpq options
*
* @example
* $params = pgsql_connection_params(
* database: 'mydb',
* host: 'localhost',
* user: 'myuser',
* password: 'secret',
* );
*/
pgsql_connection_params(string $database, string $host, int $port, ?string $user, ?string $password, array $options) : ConnectionParameters /**
* Create a default constructor-based row mapper.
*
* Maps database rows directly to constructor parameters.
* Column names must match parameter names exactly (1:1).
* Use SQL aliases if column names differ from parameter names.
*
* @example
* // DTO where column names match parameter names
* readonly class User {
* public function __construct(
* public int $id,
* public string $name,
* public string $email,
* ) {}
* }
*
* // Usage
* $client = pgsql_client(pgsql_connection('...'), mapper: pgsql_mapper());
*
* // For snake_case columns, use SQL aliases
* $user = $client->fetchInto(
* User::class,
* 'SELECT id, user_name AS name, user_email AS email FROM users WHERE id = $1',
* [1]
* );
*/
pgsql_mapper() : ConstructorMapper /**
* Create a PREPARE TRANSACTION builder.
*
* Example: prepare_transaction('my_transaction')
* Produces: PREPARE TRANSACTION 'my_transaction'
*/
prepare_transaction(string $transactionId) : PreparedTransactionFinalStep /**
* Create a PRIMARY KEY constraint.
*
* @param string ...$columns Columns that form the primary key
*/
primary_key(string $columns) : PrimaryKeyConstraint /**
* Create a raw SQL condition (use with caution).
*
* SECURITY WARNING: This function accepts raw SQL without parameterization.
* SQL injection is possible if used with untrusted user input.
* Only use with trusted, validated input.
*
* For user-provided values, use standard condition functions with param():
* ```php
* // UNSAFE - SQL injection possible:
* raw_cond("status = '" . $userInput . "'")
*
* // SAFE - use typed conditions:
* eq(col('status'), param(1))
* ```
*/
raw_cond(string $sql) : RawCondition /**
* Create a raw SQL expression (use with caution).
*
* SECURITY WARNING: This function accepts raw SQL without parameterization.
* SQL injection is possible if used with untrusted user input.
* Only use with trusted, validated input.
*
* For user-provided values, use param() instead:
* ```php
* // UNSAFE - SQL injection possible:
* raw_expr("custom_func('" . $userInput . "')")
*
* // SAFE - use parameters:
* func('custom_func', param(1))
* ```
*/
raw_expr(string $sql) : RawExpression /**
* Create a REASSIGN OWNED builder.
*
* Example: reassign_owned('old_role')->to('new_role')
* Produces: REASSIGN OWNED BY old_role TO new_role
*
* @param string ...$roles The roles whose owned objects should be reassigned
*
* @return ReassignOwnedToStep Builder for reassign owned options
*/
reassign_owned(string $roles) : ReassignOwnedToStep /**
* Get a CASCADE referential action.
*/
ref_action_cascade() : ReferentialAction /**
* Get a NO ACTION referential action.
*/
ref_action_no_action() : ReferentialAction /**
* Get a RESTRICT referential action.
*/
ref_action_restrict() : ReferentialAction /**
* Get a SET DEFAULT referential action.
*/
ref_action_set_default() : ReferentialAction /**
* Get a SET NULL referential action.
*/
ref_action_set_null() : ReferentialAction /**
* Create a POSIX regex match condition (~*).
* Case-insensitive.
*
* Example: regex_imatch(col('email'), literal_string('.*@gmail\\.com'))
*
* Produces: email ~* '.*@gmail\.com'
*/
regex_imatch(Expression $expr, Expression $pattern) : OperatorCondition /**
* Create a POSIX regex match condition (~).
* Case-sensitive.
*
* Example: regex_match(col('email'), literal_string('.*@gmail\\.com'))
*
* Produces: email ~ '.*@gmail\.com'
*/
regex_match(Expression $expr, Expression $pattern) : OperatorCondition /**
* Release a SAVEPOINT.
*
* Example: release_savepoint('my_savepoint')
* Produces: RELEASE my_savepoint
*/
release_savepoint(string $name) : SavepointFinalStep /**
* Create a RESET ROLE builder.
*
* Example: reset_role()
* Produces: RESET ROLE
*
* @return ResetRoleFinalStep Builder for reset role
*/
reset_role() : ResetRoleFinalStep /**
* Create a RETURNING clause.
*
* @param Expression ...$expressions Expressions to return
*/
returning(Expression $expressions) : ReturningClause /**
* Create a RETURNING * clause.
*/
returning_all() : ReturningClause /**
* Create a REVOKE privileges builder.
*
* Example: revoke(TablePrivilege::SELECT)->onTable('users')->from('app_user')
* Produces: REVOKE SELECT ON users FROM app_user
*
* Example: revoke(TablePrivilege::ALL)->onTable('users')->from('app_user')->cascade()
* Produces: REVOKE ALL ON users FROM app_user CASCADE
*
* @param string|TablePrivilege ...$privileges The privileges to revoke
*
* @return RevokeOnStep Builder for revoke options
*/
revoke(TablePrivilege|string $privileges) : RevokeOnStep /**
* Create a REVOKE role builder.
*
* Example: revoke_role('admin')->from('user1')
* Produces: REVOKE admin FROM user1
*
* Example: revoke_role('admin')->from('user1')->cascade()
* Produces: REVOKE admin FROM user1 CASCADE
*
* @param string ...$roles The roles to revoke
*
* @return RevokeRoleFromStep Builder for revoke role options
*/
revoke_role(string $roles) : RevokeRoleFromStep /**
* Create a ROLLBACK transaction builder.
*
* Example: rollback()->toSavepoint('my_savepoint')
* Produces: ROLLBACK TO SAVEPOINT my_savepoint
*/
rollback() : RollbackOptionsStep /**
* Create a ROLLBACK PREPARED builder.
*
* Example: rollback_prepared('my_transaction')
* Produces: ROLLBACK PREPARED 'my_transaction'
*/
rollback_prepared(string $transactionId) : PreparedTransactionFinalStep /**
* Create a row expression.
*
* @param list<Expression> $elements Row elements
*/
row_expr(array $elements) : RowExpression /**
* Create a SAVEPOINT.
*
* Example: savepoint('my_savepoint')
* Produces: SAVEPOINT my_savepoint
*/
savepoint(string $name) : SavepointFinalStep /**
* Create a new SELECT query builder.
*
* @param Expression ...$expressions Columns to select. If empty, returns SelectSelectStep.
*/
select(Expression $expressions) : SelectBuilder /**
* Create a SET ROLE builder.
*
* Example: set_role('admin')
* Produces: SET ROLE admin
*
* @param string $role The role to set
*
* @return SetRoleFinalStep Builder for set role
*/
set_role(string $role) : SetRoleFinalStep /**
* Create a SET SESSION CHARACTERISTICS AS TRANSACTION builder.
*
* Example: set_session_transaction()->isolationLevel(IsolationLevel::SERIALIZABLE)
* Produces: SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE
*/
set_session_transaction() : SetTransactionOptionsStep /**
* Create a SET TRANSACTION builder.
*
* Example: set_transaction()->isolationLevel(IsolationLevel::SERIALIZABLE)->readOnly()
* Produces: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY
*/
set_transaction() : SetTransactionOptionsStep /**
* Create a SIMILAR TO condition.
*/
similar_to(Expression $expr, Expression $pattern) : SimilarTo /**
* Create a plan analyzer for analyzing EXPLAIN plans.
*
* @param Plan $plan The execution plan to analyze
*
* @return PlanAnalyzer The analyzer for extracting insights
*/
sql_analyze(Plan $plan) : PlanAnalyzer /**
* Convert a ParsedQuery AST back to SQL string.
*
* When called without options, returns the SQL as a simple string.
* When called with DeparseOptions, applies formatting (pretty-printing, indentation, etc.).
*
* @throws \RuntimeException if deparsing fails
*/
sql_deparse(ParsedQuery $query, ?DeparseOptions $options) : string /**
* Create DeparseOptions for configuring SQL formatting.
*/
sql_deparse_options() : DeparseOptions /**
* Create an ExplainConfig for customizing EXPLAIN options.
*
* @param bool $analyze Whether to actually execute the query (ANALYZE)
* @param bool $verbose Include verbose output
* @param bool $costs Include cost estimates (default true)
* @param bool $buffers Include buffer usage statistics (requires analyze)
* @param bool $timing Include timing information (requires analyze)
* @param ExplainFormat $format Output format (JSON recommended for parsing)
*/
sql_explain_config(bool $analyze, bool $verbose, bool $costs, bool $buffers, bool $timing, ExplainFormat $format) : ExplainConfig /**
* Create an ExplainModifier for transforming queries into EXPLAIN queries.
*/
sql_explain_modifier(ExplainConfig $config) : ExplainModifier /**
* Parse EXPLAIN JSON output into a Plan object.
*
* @param string $jsonOutput The JSON output from EXPLAIN (FORMAT JSON)
*
* @return Plan The parsed execution plan
*/
sql_explain_parse(string $jsonOutput) : Plan /**
* Returns a fingerprint of the given SQL query.
* Literal values are normalized so they won't affect the fingerprint.
*/
sql_fingerprint(string $sql) : ?string /**
* Parse and format SQL query with pretty printing.
*
* This is a convenience function that parses SQL and returns it formatted.
*
* @param string $sql The SQL query to format
* @param null|DeparseOptions $options Formatting options (defaults to pretty-print enabled)
*
* @throws \RuntimeException if parsing or deparsing fails
*/
sql_format(string $sql, ?DeparseOptions $options) : string /**
* Create a KeysetColumn for keyset pagination.
*
* @param string $column Column name (can include table alias like "u.id")
* @param SortOrder $order Sort order (ASC or DESC)
*/
sql_keyset_column(string $column, SortOrder $order) : KeysetColumn /**
* Normalize SQL query by replacing literal values and named parameters with positional parameters.
* WHERE id = :id will be changed into WHERE id = $1
* WHERE id = 1 will be changed into WHERE id = $1.
*/
sql_normalize(string $sql) : ?string /**
* Normalize utility SQL statements (DDL like CREATE, ALTER, DROP).
* This handles DDL statements differently from pg_normalize() which is optimized for DML.
*/
sql_normalize_utility(string $sql) : ?string sql_parse(string $sql) : ParsedQuery sql_parser() : Parser /**
* Extract columns from a parsed SQL query.
*/
sql_query_columns(ParsedQuery $query) : Columns /**
* Get the maximum nesting depth of a SQL query.
*
* Example:
* - "SELECT * FROM t" => 1
* - "SELECT * FROM (SELECT * FROM t)" => 2
* - "SELECT * FROM (SELECT * FROM (SELECT * FROM t))" => 3
*/
sql_query_depth(string $sql) : int /**
* Extract functions from a parsed SQL query.
*/
sql_query_functions(ParsedQuery $query) : Functions /**
* Extract tables from a parsed SQL query.
*/
sql_query_tables(ParsedQuery $query) : Tables /**
* Split string with multiple SQL statements into array of individual statements.
*
* @return array<string>
*/
sql_split(string $sql) : array /**
* Generate a summary of parsed queries in protobuf format.
* Useful for query monitoring and logging without full AST overhead.
*/
sql_summary(string $sql, int $options, int $truncateLimit) : string /**
* Transform a SQL query into a COUNT query for pagination.
*
* Wraps the query in: SELECT COUNT(*) FROM (...) AS _count_subq
* Removes ORDER BY and LIMIT/OFFSET from the inner query.
*
* @param string $sql The SQL query to transform
*
* @return string The COUNT query
*/
sql_to_count_query(string $sql) : string /**
* Transform a SQL query into an EXPLAIN query.
*
* Returns the modified SQL with EXPLAIN wrapped around it.
* Defaults to EXPLAIN ANALYZE with JSON format for easy parsing.
*
* @param string $sql The SQL query to explain
* @param null|ExplainConfig $config EXPLAIN configuration (defaults to forAnalysis())
*
* @return string The EXPLAIN query
*/
sql_to_explain(string $sql, ?ExplainConfig $config) : string /**
* Transform a SQL query into a keyset (cursor-based) paginated query.
*
* More efficient than OFFSET for large datasets - uses indexed WHERE conditions.
*
* @param string $sql The SQL query to paginate (must have ORDER BY)
* @param int $limit Maximum number of rows to return
* @param list<KeysetColumn> $columns Columns for keyset pagination (must match ORDER BY)
* @param null|list<null|bool|float|int|string> $cursor Values from last row of previous page (null for first page)
*
* @return string The paginated SQL query
*/
sql_to_keyset_query(string $sql, int $limit, array $columns, ?array $cursor) : string /**
* Transform a SQL query into a paginated query with LIMIT and OFFSET.
*
* @param string $sql The SQL query to paginate
* @param int $limit Maximum number of rows to return
* @param int $offset Number of rows to skip (requires ORDER BY in query)
*
* @return string The paginated SQL query
*/
sql_to_paginated_query(string $sql, int $limit, int $offset) : string /**
* Create a SELECT * expression.
*/
star(?string $table) : Star /**
* Create a subquery expression.
*/
sub_select(SelectFinalStep $query) : Subquery /**
* Create a table reference.
*
* Supports dot notation for schema-qualified names: "public.users" or explicit schema parameter.
* Double-quoted identifiers preserve dots: '"my.table"' creates a single identifier.
*
* @param string $name Table name (may include schema as "schema.table")
* @param null|string $schema Schema name (optional, overrides parsed schema)
*/
table(string $name, ?string $schema) : Table /**
* Create a table function reference.
*
* @param FunctionCall $function The table-valued function
* @param bool $withOrdinality Whether to add WITH ORDINALITY
*/
table_func(FunctionCall $function, bool $withOrdinality) : TableFunction /**
* Create a full-text search match condition (@@).
*
* Example: text_search_match(col('document'), raw_expr("to_tsquery('english', 'hello & world')"))
* Produces: document @@ to_tsquery('english', 'hello & world')
*/
text_search_match(Expression $document, Expression $query) : OperatorCondition /**
* Create a SET TRANSACTION SNAPSHOT builder.
*
* Example: transaction_snapshot('00000003-0000001A-1')
* Produces: SET TRANSACTION SNAPSHOT '00000003-0000001A-1'
*/
transaction_snapshot(string $snapshotId) : SetTransactionFinalStep /**
* Create a TRUNCATE TABLE builder.
*
* @param string ...$tables Table names to truncate
*/
truncate_table(string $tables) : TruncateFinalStep /**
* Wrap a value with explicit type for parameter binding.
*
* Use when auto-detection isn't sufficient, e.g.:
* - String that should be UUID
* - String that should be JSON
* - DateTime that should be DATE (not TIMESTAMP)
*
* @param mixed $value The value to bind
* @param Type<mixed> $type The Flow type to use for conversion
*
* @example
* $client->fetch(
* 'SELECT * FROM users WHERE id = $1 AND metadata = $2',
* [
* typed('550e8400-e29b-41d4-a716-446655440000', type_uuid()),
* typed('{"key": "value"}', type_json()),
* ]
* );
*/
typed(?mixed $value, Type $type) : TypedValue /**
* Creates a type attribute for composite types.
*
* Example: type_attr('name', data_type_text())
* Produces: name text
*
* Example: type_attr('description', data_type_text())->collate('en_US')
* Produces: description text COLLATE "en_US"
*
* @param string $name The attribute name
* @param DataType $type The attribute type
*
* @return TypeAttribute Type attribute value object
*/
type_attr(string $name, DataType $type) : TypeAttribute /**
* Create a UNIQUE constraint.
*
* @param string ...$columns Columns that must be unique together
*/
unique_constraint(string $columns) : UniqueConstraint /**
* Create a new UPDATE query builder.
*/
update() : UpdateTableStep /**
* Create a VACUUM builder.
*
* Example: vacuum()->table('users')
* Produces: VACUUM users
*/
vacuum() : VacuumFinalStep /**
* Create a VALUES clause as a table reference.
*
* Usage:
* select()->from(
* values_table(
* row_expr([literal(1), literal('Alice')]),
* row_expr([literal(2), literal('Bob')])
* )->as('t', ['id', 'name'])
* )
*
* Generates: SELECT * FROM (VALUES (1, 'Alice'), (2, 'Bob')) AS t(id, name)
*/
values_table(RowExpression $rows) : ValuesTable /**
* Create a WHEN clause for CASE expression.
*/
when(Expression $condition, Expression $result) : WhenClause /**
* Create a window definition for WINDOW clause.
*
* @param string $name Window name
* @param list<Expression> $partitionBy PARTITION BY expressions
* @param list<OrderBy|OrderByItem> $orderBy ORDER BY items
* @param null|WindowFrame $frame Window frame specification
*/
window_def(string $name, array $partitionBy, array $orderBy, ?WindowFrame $frame) : WindowDefinition /**
* Create a window frame specification.
*/
window_frame(FrameMode $mode, FrameBound $start, ?FrameBound $end, FrameExclusion $exclusion) : WindowFrame /**
* Create a window function.
*
* @param string $name Function name
* @param list<Expression> $args Function arguments
* @param list<Expression> $partitionBy PARTITION BY expressions
* @param list<OrderBy|OrderByItem> $orderBy ORDER BY items
*/
window_func(string $name, array $args, array $partitionBy, array $orderBy) : WindowFunction /**
* Create a WITH clause builder for CTEs.
*
* Example: with(cte('users', $subquery))->select(star())->from(table('users'))
* Example: with(cte('a', $q1), cte('b', $q2))->recursive()->select(...)->from(table('a'))
*/
with(CTE $ctes) : WithBuilder