diff --git a/tests/WP_SQLite_Driver_Translation_Tests.php b/tests/WP_SQLite_Driver_Translation_Tests.php
index ca41765e..1da6334b 100644
--- a/tests/WP_SQLite_Driver_Translation_Tests.php
+++ b/tests/WP_SQLite_Driver_Translation_Tests.php
@@ -286,6 +286,55 @@ public function testCreateTable(): void {
 		);
 	}
 
+	public function testAlterTable(): void {
+		// Prepare a real table, so we can test multi-operation alter statements.
+		// Otherwise, we'd hit and exception and rollback after the first query.
+		$this->assertQuery(
+			'CREATE TABLE "t" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT )',
+			'CREATE TABLE t (id INT PRIMARY KEY AUTO_INCREMENT)'
+		);
+
+		// ADD COLUMN.
+		$this->assertQuery(
+			'ALTER TABLE "t" ADD COLUMN "a" INTEGER',
+			'ALTER TABLE t ADD a INT'
+		);
+
+		// ADD COLUMN with multiple columns.
+		$this->assertQuery(
+			array(
+				'ALTER TABLE "t" ADD COLUMN "b" INTEGER',
+				'ALTER TABLE "t" ADD COLUMN "c" TEXT',
+				'ALTER TABLE "t" ADD COLUMN "d" INTEGER',
+			),
+			'ALTER TABLE t ADD b INT, ADD c TEXT, ADD d BOOL'
+		);
+
+		// DROP COLUMN.
+		$this->assertQuery(
+			'ALTER TABLE "t" DROP COLUMN "a"',
+			'ALTER TABLE t DROP a'
+		);
+
+		// DROP COLUMN with multiple columns.
+		$this->assertQuery(
+			array(
+				'ALTER TABLE "t" DROP COLUMN "b"',
+				'ALTER TABLE "t" DROP COLUMN "c"',
+			),
+			'ALTER TABLE t DROP b, DROP c'
+		);
+
+		// ADD COLUMN and DROP COLUMN combined.
+		$this->assertQuery(
+			array(
+				'ALTER TABLE "t" ADD COLUMN "a" INTEGER',
+				'ALTER TABLE "t" DROP COLUMN "d"',
+			),
+			'ALTER TABLE t ADD a INT, DROP d'
+		);
+	}
+
 	public function testDataTypes(): void {
 		// Numeric data types.
 		$this->assertQuery(
diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-driver.php b/wp-includes/sqlite-ast/class-wp-sqlite-driver.php
index 3858efa4..0cb250a2 100644
--- a/wp-includes/sqlite-ast/class-wp-sqlite-driver.php
+++ b/wp-includes/sqlite-ast/class-wp-sqlite-driver.php
@@ -792,6 +792,36 @@ private function execute_mysql_query( WP_Parser_Node $ast ) {
 						);
 				}
 				break;
+			case 'alterStatement':
+				$this->query_type = 'ALTER';
+				$subtree          = $ast->get_child_node();
+				switch ( $subtree->rule_name ) {
+					case 'alterTable':
+						$this->execute_alter_table_statement( $ast );
+						break;
+					default:
+						throw new Exception(
+							sprintf(
+								'Unsupported statement type: "%s" > "%s"',
+								$ast->rule_name,
+								$subtree->rule_name
+							)
+						);
+				}
+				break;
+			case 'dropStatement':
+				$this->query_type = 'DROP';
+				$query            = $this->translate( $ast );
+				$this->execute_sqlite_query( $query );
+				$this->set_result_from_affected_rows();
+				break;
+			case 'setStatement':
+				/*
+				 * It would be lovely to support at least SET autocommit,
+				 * but I don't think that is even possible with SQLite.
+				 */
+				$this->results = 0;
+				break;
 			default:
 				throw new Exception( sprintf( 'Unsupported statement type: "%s"', $ast->rule_name ) );
 		}
@@ -934,6 +964,68 @@ private function execute_create_table_statement( WP_Parser_Node $node ): void {
 		$this->set_result_from_affected_rows();
 	}
 
+	private function execute_alter_table_statement( WP_Parser_Node $node ): void {
+		$table_name = $this->translate( $node->get_descendant_node( 'tableRef' ) );
+		$actions    = $node->get_descendant_nodes( 'alterListItem' );
+
+		/*
+		 * SQLite supports only a small subset of MySQL ALTER TABLE statement.
+		 * We need to handle some differences and emulate some operations:
+		 *
+		 * 1. Multiple operations in a single ALTER TABLE statement.
+		 *
+		 *  SQLite doesn't support multiple operations in a single ALTER TABLE
+		 *  statement. We need to execute each operation as a separate query.
+		 *
+		 * 2. ADD COLUMN in SQLite doesn't support some valid MySQL constructs:
+		 *
+		 *  - Adding a column with PRIMARY KEY or UNIQUE constraint.
+		 *  - Adding a column with AUTOINCREMENT.
+		 *  - Adding a column with CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP,
+		 *    or an expression in parentheses as a default value.
+		 *  - Adding a NOT NULL column without a default value when the table is
+		 *    not empty. In MySQL, this depends on the data type and SQL mode.
+		 *
+		 *    @TODO: Address these nuances.
+		 */
+		foreach ( $actions as $action ) {
+			$token = $action->get_child_token();
+
+			// ADD column/constraint.
+			if ( WP_MySQL_Lexer::ADD_SYMBOL === $token->id ) {
+				// ADD COLUMN.
+				$field_definition = $action->get_descendant_node( 'fieldDefinition' );
+				if ( null !== $field_definition ) {
+					$field_name = $this->translate( $action->get_child_node( 'identifier' ) );
+					$field      = $this->translate( $field_definition );
+					$this->execute_sqlite_query(
+						sprintf( 'ALTER TABLE %s ADD COLUMN %s %s', $table_name, $field_name, $field )
+					);
+				}
+
+				// ADD CONSTRAINT.
+				$constraint = $action->get_descendant_node( 'tableConstraintDef' );
+				if ( null !== $constraint ) {
+					$constraint_name = $this->translate( $constraint->get_child_node( 'identifier' ) );
+					$constraint      = $this->translate( $constraint );
+					$this->execute_sqlite_query(
+						sprintf( 'ALTER TABLE %s ADD CONSTRAINT %s %s', $table_name, $constraint_name, $constraint )
+					);
+				}
+			} elseif ( WP_MySQL_Lexer::DROP_SYMBOL === $token->id ) {
+				// DROP COLUMN.
+				$field_name = $this->translate( $action->get_child_node( 'columnInternalRef' ) );
+				if ( null !== $field_name ) {
+					$this->execute_sqlite_query(
+						sprintf( 'ALTER TABLE %s DROP COLUMN %s', $table_name, $field_name )
+					);
+				}
+			}
+		}
+
+		$this->set_result_from_affected_rows();
+	}
+
 	private function translate( $ast ) {
 		if ( null === $ast ) {
 			return null;