Skip to content

Commit 0f69c36

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 7fc2a3f commit 0f69c36

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
@@ -4285,6 +4285,13 @@ private function translate_simple_expr_body( WP_Parser_Node $node ): string {
42854285
return sprintf( '%s COLLATE BINARY', $this->translate( $expr ) );
42864286
}
42874287

4288+
// Translate "CAST(expr AS type)" to its SQLite equivalent.
4289+
if ( null !== $token && WP_MySQL_Lexer::CAST_SYMBOL === $token->id ) {
4290+
$expr = $node->get_first_child_node( 'expr' );
4291+
$cast_type = $node->get_first_child_node( 'castType' );
4292+
return $this->translate_cast_expr( $expr, $cast_type );
4293+
}
4294+
42884295
/**
42894296
* Translate MySQL CONVERT() expression.
42904297
*
@@ -4293,23 +4300,44 @@ private function translate_simple_expr_body( WP_Parser_Node $node ): string {
42934300
* 2. CONVERT(expr USING charset): Converts the character set.
42944301
*/
42954302
if ( null !== $token && WP_MySQL_Lexer::CONVERT_SYMBOL === $token->id ) {
4296-
$expr = $this->translate( $node->get_first_child_node( 'expr' ) );
4303+
$expr = $node->get_first_child_node( 'expr' );
42974304
$cast_type = $node->get_first_child_node( 'castType' );
42984305

42994306
if ( null !== $cast_type ) {
43004307
// CONVERT(expr, type): Translate to cast expression.
43014308
// TODO: Emulate UNSIGNED cast. SQLite has no unsigned integer type.
4302-
return sprintf( 'CAST(%s AS %s)', $expr, $this->translate( $cast_type ) );
4309+
return $this->translate_cast_expr( $expr, $cast_type );
43034310
} else {
43044311
// CONVERT(expr USING charset): Keep "expr" as is (no SQLite support).
43054312
// TODO: Consider rejecting UTF-8-incompatible charasets.
4306-
return $expr;
4313+
return $this->translate( $expr );
43074314
}
43084315
}
43094316

43104317
return $this->translate_sequence( $node->get_children() );
43114318
}
43124319

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

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)