@@ -151,6 +151,51 @@ class WP_SQLite_Driver {
151
151
'geometrycollection ' => 'TEXT ' ,
152
152
);
153
153
154
+ /**
155
+ * The MySQL to SQLite date formats translation.
156
+ *
157
+ * Maps MySQL formats to SQLite strftime() formats.
158
+ *
159
+ * For MySQL formats, see:
160
+ * * https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format
161
+ *
162
+ * For SQLite formats, see:
163
+ * * https://www.sqlite.org/lang_datefunc.html
164
+ * * https://strftime.org/
165
+ */
166
+ const DATE_FORMAT_TO_STRFTIME_MAP = array (
167
+ '%a ' => '%D ' ,
168
+ '%b ' => '%M ' ,
169
+ '%c ' => '%n ' ,
170
+ '%D ' => '%jS ' ,
171
+ '%d ' => '%d ' ,
172
+ '%e ' => '%j ' ,
173
+ '%H ' => '%H ' ,
174
+ '%h ' => '%h ' ,
175
+ '%I ' => '%h ' ,
176
+ '%i ' => '%M ' ,
177
+ '%j ' => '%z ' ,
178
+ '%k ' => '%G ' ,
179
+ '%l ' => '%g ' ,
180
+ '%M ' => '%F ' ,
181
+ '%m ' => '%m ' ,
182
+ '%p ' => '%A ' ,
183
+ '%r ' => '%h:%i:%s %A ' ,
184
+ '%S ' => '%s ' ,
185
+ '%s ' => '%s ' ,
186
+ '%T ' => '%H:%i:%s ' ,
187
+ '%U ' => '%W ' ,
188
+ '%u ' => '%W ' ,
189
+ '%V ' => '%W ' ,
190
+ '%v ' => '%W ' ,
191
+ '%W ' => '%l ' ,
192
+ '%w ' => '%w ' ,
193
+ '%X ' => '%Y ' ,
194
+ '%x ' => '%o ' ,
195
+ '%Y ' => '%Y ' ,
196
+ '%y ' => '%y ' ,
197
+ );
198
+
154
199
const DATA_TYPES_CACHE_TABLE = '_mysql_data_types_cache ' ;
155
200
156
201
const CREATE_DATA_TYPES_CACHE_TABLE = 'CREATE TABLE IF NOT EXISTS _mysql_data_types_cache (
@@ -1323,6 +1368,8 @@ private function translate( $ast ) {
1323
1368
return $ this ->translate_sequence ( $ ast ->get_children () );
1324
1369
case 'runtimeFunctionCall ' :
1325
1370
return $ this ->translate_runtime_function_call ( $ ast );
1371
+ case 'functionCall ' :
1372
+ return $ this ->translate_function_call ( $ ast );
1326
1373
case 'systemVariable ' :
1327
1374
// @TODO: Emulate some system variables, or use reasonable defaults.
1328
1375
// See: https://dev.mysql.com/doc/refman/8.4/en/server-system-variable-reference.html
@@ -1478,6 +1525,59 @@ private function translate_runtime_function_call( WP_Parser_Node $node ): string
1478
1525
}
1479
1526
}
1480
1527
1528
+ private function translate_function_call ( WP_Parser_Node $ node ): string {
1529
+ $ nodes = $ node ->get_child_nodes ();
1530
+ $ name = strtoupper (
1531
+ $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ nodes [0 ] ) )
1532
+ );
1533
+
1534
+ $ args = array ();
1535
+ foreach ( $ nodes [1 ]->get_child_nodes () as $ child ) {
1536
+ $ args [] = $ this ->translate ( $ child );
1537
+ }
1538
+
1539
+ switch ( $ name ) {
1540
+ case 'DATE_FORMAT ' :
1541
+ list ( $ date , $ mysql_format ) = $ args ;
1542
+
1543
+ $ format = strtr ( $ mysql_format , self ::DATE_FORMAT_TO_STRFTIME_MAP );
1544
+ if ( ! $ format ) {
1545
+ throw new Exception ( "Could not translate a DATE_FORMAT() format to STRFTIME format ( $ mysql_format) " );
1546
+ }
1547
+
1548
+ /*
1549
+ * MySQL supports comparing strings and floats, e.g.
1550
+ *
1551
+ * > SELECT '00.42' = 0.4200
1552
+ * 1
1553
+ *
1554
+ * SQLite does not support that. At the same time,
1555
+ * WordPress likes to filter dates by comparing numeric
1556
+ * outputs of DATE_FORMAT() to floats, e.g.:
1557
+ *
1558
+ * -- Filter by hour and minutes
1559
+ * DATE_FORMAT(
1560
+ * STR_TO_DATE('2014-10-21 00:42:29', '%Y-%m-%d %H:%i:%s'),
1561
+ * '%H.%i'
1562
+ * ) = 0.4200;
1563
+ *
1564
+ * Let's cast the STRFTIME() output to a float if
1565
+ * the date format is typically used for string
1566
+ * to float comparisons.
1567
+ *
1568
+ * In the future, let's update WordPress to avoid comparing
1569
+ * strings and floats.
1570
+ */
1571
+ $ cast_to_float = "'%H.%i' " === $ mysql_format ;
1572
+ if ( true === $ cast_to_float ) {
1573
+ return sprintf ( 'CAST(STRFTIME(%s, %s) AS FLOAT) ' , $ format , $ date );
1574
+ }
1575
+ return sprintf ( 'STRFTIME(%s, %s) ' , $ format , $ date );
1576
+ default :
1577
+ return $ this ->translate_sequence ( $ node ->get_children () );
1578
+ }
1579
+ }
1580
+
1481
1581
private function get_sqlite_create_table_statement ( string $ table_name , ?string $ new_table_name = null ): array {
1482
1582
// 1. Get table info.
1483
1583
$ table_info = $ this ->execute_sqlite_query (
0 commit comments