Skip to content

Commit 5608b8f

Browse files
committed
Emulate "columnN" naming for VALUES lists on SQLite < 3.33.0
1 parent 93f55b2 commit 5608b8f

2 files changed

Lines changed: 79 additions & 17 deletions

File tree

tests/WP_SQLite_Driver_Translation_Tests.php

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,23 +107,33 @@ public function testInsert(): void {
107107
$this->driver->query( 'CREATE TABLE t2 (c1 INT, c2 INT)' );
108108
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
109109

110+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
111+
110112
$this->assertQuery(
111-
'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
113+
$is_values_naming_supported
114+
? 'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
115+
: 'INSERT INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
112116
'INSERT INTO t (c) VALUES (1)'
113117
);
114118

115119
$this->assertQuery(
116-
'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
120+
$is_values_naming_supported
121+
? 'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
122+
: 'INSERT INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
117123
'INSERT INTO wp.t (c) VALUES (1)'
118124
);
119125

120126
$this->assertQuery(
121-
'INSERT INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true',
127+
$is_values_naming_supported
128+
? 'INSERT INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true'
129+
: 'INSERT INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (SELECT NULL AS `column1`, NULL AS `column2` WHERE FALSE UNION ALL VALUES ( 1 , 2 )) WHERE true',
122130
'INSERT INTO t (c1, c2) VALUES (1, 2)'
123131
);
124132

125133
$this->assertQuery(
126-
'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
134+
$is_values_naming_supported
135+
? 'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
136+
: 'INSERT INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
127137
'INSERT INTO t (c) VALUES (1), (2)'
128138
);
129139

@@ -141,18 +151,26 @@ public function testInsertWithTypeCasting(): void {
141151
$this->driver->query( 'CREATE TABLE t2 (c1 TEXT, c2 TEXT)' );
142152
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
143153

154+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
155+
144156
$this->assertQuery(
145-
'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true',
157+
$is_values_naming_supported
158+
? 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true'
159+
: 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
146160
'INSERT INTO t1 (c1) VALUES (1)'
147161
);
148162

149163
$this->assertQuery(
150-
'INSERT INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true',
164+
$is_values_naming_supported
165+
? 'INSERT INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true'
166+
: 'INSERT INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (SELECT NULL AS `column1`, NULL AS `column2` WHERE FALSE UNION ALL VALUES ( 1 , 2 )) WHERE true',
151167
'INSERT INTO t1 (c1, c2) VALUES (1, 2)'
152168
);
153169

154170
$this->assertQuery(
155-
'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
171+
$is_values_naming_supported
172+
? 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
173+
: 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
156174
'INSERT INTO t1 (c1) VALUES (1), (2)'
157175
);
158176

@@ -171,23 +189,33 @@ public function testReplace(): void {
171189
$this->driver->query( 'CREATE TABLE t2 (c1 INT, c2 INT)' );
172190
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
173191

192+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
193+
174194
$this->assertQuery(
175-
'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
195+
$is_values_naming_supported
196+
? 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
197+
: 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
176198
'REPLACE INTO t (c) VALUES (1)'
177199
);
178200

179201
$this->assertQuery(
180-
'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
202+
$is_values_naming_supported
203+
? 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
204+
: 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
181205
'REPLACE INTO wp.t (c) VALUES (1)'
182206
);
183207

184208
$this->assertQuery(
185-
'REPLACE INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true',
209+
$is_values_naming_supported
210+
? 'REPLACE INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true'
211+
: 'REPLACE INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (SELECT NULL AS `column1`, NULL AS `column2` WHERE FALSE UNION ALL VALUES ( 1 , 2 )) WHERE true',
186212
'REPLACE INTO t (c1, c2) VALUES (1, 2)'
187213
);
188214

189215
$this->assertQuery(
190-
'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
216+
$is_values_naming_supported
217+
? 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
218+
: 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
191219
'REPLACE INTO t (c) VALUES (1), (2)'
192220
);
193221

@@ -205,18 +233,26 @@ public function testReplaceWithTypeCasting(): void {
205233
$this->driver->query( 'CREATE TABLE t2 (c1 TEXT, c2 TEXT)' );
206234
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
207235

236+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
237+
208238
$this->assertQuery(
209-
'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true',
239+
$is_values_naming_supported
240+
? 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true'
241+
: 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
210242
'REPLACE INTO t1 (c1) VALUES (1)'
211243
);
212244

213245
$this->assertQuery(
214-
'REPLACE INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true',
246+
$is_values_naming_supported
247+
? 'REPLACE INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true'
248+
: 'REPLACE INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (SELECT NULL AS `column1`, NULL AS `column2` WHERE FALSE UNION ALL VALUES ( 1 , 2 )) WHERE true',
215249
'REPLACE INTO t1 (c1, c2) VALUES (1, 2)'
216250
);
217251

218252
$this->assertQuery(
219-
'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
253+
$is_values_naming_supported
254+
? 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
255+
: 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
220256
'REPLACE INTO t1 (c1) VALUES (1), (2)'
221257
);
222258

wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4872,9 +4872,35 @@ function ( $column ) use ( $is_strict_mode, $insert_map ) {
48724872
// Wrap the original insert VALUES, SELECT, or SET list in a FROM clause.
48734873
if ( 'insertFromConstructor' === $node->rule_name ) {
48744874
// VALUES (...)
4875-
$from = $this->translate(
4876-
$node->get_first_child_node( 'insertValues' )
4877-
);
4875+
$insert_values = $node->get_first_child_node( 'insertValues' );
4876+
$from = $this->translate( $insert_values );
4877+
4878+
/**
4879+
* The automatic "columnN" naming for VALUES lists is supported only
4880+
* from SQLite 3.33.0. For older versions, we need to emulate it by
4881+
* prepending a dummy VALUES list header via the UNION ALL operator:
4882+
*
4883+
* SELECT
4884+
* NULL AS `column1`, NULL AS `column2`, ... WHERE FALSE
4885+
* UNION ALL
4886+
* VALUES (value1, value2, ...)
4887+
*/
4888+
$is_values_naming_supported = version_compare( $this->get_sqlite_version(), '3.33.0', '>=' );
4889+
if ( ! $is_values_naming_supported ) {
4890+
$values_list = $insert_values->get_first_child_node( 'valueList' );
4891+
$values = $values_list->get_first_child_node( 'values' );
4892+
$value_count = (
4893+
count( $values->get_child_nodes( 'expr' ) )
4894+
+ count( $values->get_child_nodes( WP_MySQL_Lexer::DEFAULT_SYMBOL ) )
4895+
);
4896+
4897+
$columns_list = '';
4898+
for ( $i = 1; $i <= $value_count; $i++ ) {
4899+
$columns_list .= $i > 1 ? ', ' : '';
4900+
$columns_list .= 'NULL AS ' . $this->quote_sqlite_identifier( 'column' . $i );
4901+
}
4902+
$from = 'SELECT ' . $columns_list . ' WHERE FALSE UNION ALL ' . $from;
4903+
}
48784904
} elseif ( 'insertQueryExpression' === $node->rule_name ) {
48794905
// SELECT ...
48804906
$from = $this->translate(

0 commit comments

Comments
 (0)