Skip to content

Commit 1b306a2

Browse files
committed
Translate CAST AS BINARY via COLLATE BINARY
Replace 'CAST(x AS BLOB)' with 'CAST(x AS TEXT) COLLATE BINARY' for MySQL BINARY cast targets (both CAST(...) and CONVERT(...) forms). The previous BLOB form caused TEXT-vs-BLOB equality to always return FALSE due to SQLite's storage-class ordering. Refs #31.
1 parent f53ebed commit 1b306a2

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4287,6 +4287,13 @@ private function translate_simple_expr_body( WP_Parser_Node $node ): string {
42874287
);
42884288
}
42894289

4290+
// Translate "CAST(expr AS type)" to its SQLite equivalent.
4291+
if ( null !== $token && WP_MySQL_Lexer::CAST_SYMBOL === $token->id ) {
4292+
$expr = $node->get_first_child_node( 'expr' );
4293+
$cast_type = $node->get_first_child_node( 'castType' );
4294+
return $this->translate_cast_expr( $expr, $cast_type );
4295+
}
4296+
42904297
/**
42914298
* Translate MySQL CONVERT() expression.
42924299
*
@@ -4295,23 +4302,44 @@ private function translate_simple_expr_body( WP_Parser_Node $node ): string {
42954302
* 2. CONVERT(expr USING charset): Converts the character set.
42964303
*/
42974304
if ( null !== $token && WP_MySQL_Lexer::CONVERT_SYMBOL === $token->id ) {
4298-
$expr = $this->translate( $node->get_first_child_node( 'expr' ) );
4305+
$expr = $node->get_first_child_node( 'expr' );
42994306
$cast_type = $node->get_first_child_node( 'castType' );
43004307

43014308
if ( null !== $cast_type ) {
43024309
// CONVERT(expr, type): Translate to cast expression.
43034310
// TODO: Emulate UNSIGNED cast. SQLite has no unsigned integer type.
4304-
return sprintf( 'CAST(%s AS %s)', $expr, $this->translate( $cast_type ) );
4311+
return $this->translate_cast_expr( $expr, $cast_type );
43054312
} else {
43064313
// CONVERT(expr USING charset): Keep "expr" as is (no SQLite support).
43074314
// TODO: Consider rejecting UTF-8-incompatible charasets.
4308-
return $expr;
4315+
return $this->translate( $expr );
43094316
}
43104317
}
43114318

43124319
return $this->translate_sequence( $node->get_children() );
43134320
}
43144321

4322+
/**
4323+
* Translate a MySQL CAST expression to SQLite.
4324+
*
4325+
* Shared by the CAST(expr AS type) and CONVERT(expr, type) forms.
4326+
*
4327+
* @param WP_Parser_Node $expr The "expr" AST node.
4328+
* @param WP_Parser_Node $cast_type The "castType" AST node.
4329+
* @return string The translated SQLite expression.
4330+
*/
4331+
private function translate_cast_expr( WP_Parser_Node $expr, WP_Parser_Node $cast_type ): string {
4332+
/*
4333+
* Translate "CAST(expr AS BINARY)" to "CAST(expr AS TEXT) COLLATE BINARY".
4334+
* Emitting "CAST(expr AS BLOB)" would break equality against TEXT values
4335+
* due to SQLite's storage-class ordering (BLOB > TEXT).
4336+
*/
4337+
if ( $cast_type->has_child_token( WP_MySQL_Lexer::BINARY_SYMBOL ) ) {
4338+
return sprintf( 'CAST(%s AS TEXT) COLLATE BINARY', $this->translate( $expr ) );
4339+
}
4340+
return sprintf( 'CAST(%s AS %s)', $this->translate( $expr ), $this->translate( $cast_type ) );
4341+
}
4342+
43154343
/**
43164344
* Translate a MySQL LIKE expression to SQLite.
43174345
*

packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Translation_Tests.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public function testSelect(): void {
104104
public function testConvert(): void {
105105
// CONVERT(expr, type) → CAST(expr AS type)
106106
$this->assertQuery(
107-
"SELECT CAST('abc' AS BLOB) AS `CONVERT('abc', BINARY)`",
107+
"SELECT CAST('abc' AS TEXT) COLLATE BINARY AS `CONVERT('abc', BINARY)`",
108108
"SELECT CONVERT('abc', BINARY)"
109109
);
110110

@@ -172,6 +172,18 @@ public function testBinary(): void {
172172
"SELECT ( `a` || `b` ) COLLATE BINARY = 'x' AS `BINARY (a || b) = 'x'` FROM `t`",
173173
"SELECT BINARY (a || b) = 'x' FROM t"
174174
);
175+
176+
// "CAST(expr AS BINARY)" → "CAST(expr AS TEXT) COLLATE BINARY"
177+
$this->assertQuery(
178+
"SELECT CAST('abc' AS TEXT) COLLATE BINARY AS `CAST('abc' AS BINARY)`",
179+
"SELECT CAST('abc' AS BINARY)"
180+
);
181+
182+
// "CAST(expr AS BINARY) = expr" → "CAST(expr AS TEXT) COLLATE BINARY = expr"
183+
$this->assertQuery(
184+
"SELECT CAST('abc' AS TEXT) COLLATE BINARY = 'abc' AS `CAST('abc' AS BINARY) = 'abc'`",
185+
"SELECT CAST('abc' AS BINARY) = 'abc'"
186+
);
175187
}
176188

177189
public function testInsert(): void {

0 commit comments

Comments
 (0)