@@ -45,6 +45,10 @@ protected function tearDown(): void
4545 {
4646 parent ::tearDown ();
4747
48+ if (! $ this ->query instanceof BasePreparedQuery) {
49+ return ;
50+ }
51+
4852 try {
4953 $ this ->query ->close ();
5054 } catch (BadMethodCallException ) {
@@ -109,6 +113,70 @@ public function testPrepareReturnsManualPreparedQuery(): void
109113 $ this ->assertSame ($ expected , $ this ->query ->getQueryString ());
110114 }
111115
116+ public function testPrepareAndExecuteManualQueryWithNamedPlaceholdersKeepsTimeLiteral (): void
117+ {
118+ // Quote alias to keep a consistent property name across drivers (OCI8 uppercases unquoted aliases)
119+ $ timeValue = $ this ->db ->protectIdentifiers ('time_value ' );
120+ $ this ->query = $ this ->db ->prepare (static function ($ db ) use ($ timeValue ): Query {
121+ $ sql = 'SELECT '
122+ . $ db ->protectIdentifiers ('name ' ) . ', '
123+ . $ db ->protectIdentifiers ('email ' )
124+ . ", '12:34' AS " . $ timeValue . ' '
125+ . 'FROM ' . $ db ->protectIdentifiers ($ db ->DBPrefix . 'user ' )
126+ . ' WHERE '
127+ . $ db ->protectIdentifiers ('name ' ) . ' = :name: '
128+ . ' AND ' . $ db ->protectIdentifiers ('email ' ) . ' = :email ' ;
129+
130+ return (new Query ($ db ))->setQuery ($ sql );
131+ });
132+
133+ $ preparedSql = $ this ->query ->getQueryString ();
134+
135+ $ this ->assertStringContainsString ("'12:34' AS " . $ timeValue , $ preparedSql );
136+
137+ if ($ this ->db ->DBDriver === 'Postgre ' ) {
138+ $ this ->assertStringContainsString (' = $1 ' , $ preparedSql );
139+ $ this ->assertStringContainsString (' = $2 ' , $ preparedSql );
140+ } else {
141+ $ this ->assertStringContainsString (' = ? ' , $ preparedSql );
142+ }
143+
144+ $ result = $ this ->query ->execute ('Derek Jones ' , 'derek@world.com ' );
145+
146+ $ this ->assertInstanceOf (ResultInterface::class, $ result );
147+ $ this ->assertSame ('Derek Jones ' , $ result ->getRow ()->name );
148+ $ this ->assertSame ('derek@world.com ' , $ result ->getRow ()->email );
149+ $ this ->assertSame ('12:34 ' , $ result ->getRow ()->time_value );
150+ }
151+
152+ public function testPrepareAndExecuteManualQueryWithPostgreCastKeepsDoubleColonSyntax (): void
153+ {
154+ if ($ this ->db ->DBDriver !== 'Postgre ' ) {
155+ $ this ->markTestSkipped ('PostgreSQL-specific cast syntax test. ' );
156+ }
157+
158+ $ this ->query = $ this ->db ->prepare (static function ($ db ): Query {
159+ $ sql = 'SELECT '
160+ . ':value: AS value, now()::timestamp AS created_at '
161+ . ' FROM ' . $ db ->protectIdentifiers ($ db ->DBPrefix . 'user ' )
162+ . ' WHERE ' . $ db ->protectIdentifiers ('name ' ) . ' = :name: ' ;
163+
164+ return (new Query ($ db ))->setQuery ($ sql );
165+ });
166+
167+ $ preparedSql = $ this ->query ->getQueryString ();
168+
169+ $ this ->assertStringContainsString ('$1 AS value ' , $ preparedSql );
170+ $ this ->assertStringContainsString ('now()::timestamp AS created_at ' , $ preparedSql );
171+
172+ $ result = $ this ->query ->execute ('ci4 ' , 'Derek Jones ' );
173+
174+ $ this ->assertInstanceOf (ResultInterface::class, $ result );
175+ $ this ->assertSame ('ci4 ' , $ result ->getRow ()->value );
176+ $ this ->assertNotEmpty ($ result ->getRow ()->created_at );
177+ $ this ->assertNotSame ('now()::timestamp ' , $ result ->getRow ()->created_at );
178+ }
179+
112180 public function testExecuteRunsQueryAndReturnsTrue (): void
113181 {
114182 $ this ->query = $ this ->db ->prepare (static fn ($ db ) => $ db ->table ('user ' )->insert ([
0 commit comments