Skip to content

Commit e869621

Browse files
committed
Emulate "columnN" naming for VALUES lists on SQLite < 3.33.0
1 parent c59c36f commit e869621

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
@@ -102,23 +102,33 @@ public function testInsert(): void {
102102
$this->driver->query( 'CREATE TABLE t2 (c1 INT, c2 INT)' );
103103
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
104104

105+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
106+
105107
$this->assertQuery(
106-
'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
108+
$is_values_naming_supported
109+
? 'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
110+
: 'INSERT INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
107111
'INSERT INTO t (c) VALUES (1)'
108112
);
109113

110114
$this->assertQuery(
111-
'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
115+
$is_values_naming_supported
116+
? 'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
117+
: 'INSERT INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
112118
'INSERT INTO wp.t (c) VALUES (1)'
113119
);
114120

115121
$this->assertQuery(
116-
'INSERT INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true',
122+
$is_values_naming_supported
123+
? 'INSERT INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true'
124+
: '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',
117125
'INSERT INTO t (c1, c2) VALUES (1, 2)'
118126
);
119127

120128
$this->assertQuery(
121-
'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
129+
$is_values_naming_supported
130+
? 'INSERT INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
131+
: 'INSERT INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
122132
'INSERT INTO t (c) VALUES (1), (2)'
123133
);
124134

@@ -136,18 +146,26 @@ public function testInsertWithTypeCasting(): void {
136146
$this->driver->query( 'CREATE TABLE t2 (c1 TEXT, c2 TEXT)' );
137147
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
138148

149+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
150+
139151
$this->assertQuery(
140-
'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true',
152+
$is_values_naming_supported
153+
? 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true'
154+
: 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
141155
'INSERT INTO t1 (c1) VALUES (1)'
142156
);
143157

144158
$this->assertQuery(
145-
'INSERT INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true',
159+
$is_values_naming_supported
160+
? 'INSERT INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true'
161+
: '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',
146162
'INSERT INTO t1 (c1, c2) VALUES (1, 2)'
147163
);
148164

149165
$this->assertQuery(
150-
'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
166+
$is_values_naming_supported
167+
? 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
168+
: 'INSERT INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
151169
'INSERT INTO t1 (c1) VALUES (1), (2)'
152170
);
153171

@@ -166,23 +184,33 @@ public function testReplace(): void {
166184
$this->driver->query( 'CREATE TABLE t2 (c1 INT, c2 INT)' );
167185
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
168186

187+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
188+
169189
$this->assertQuery(
170-
'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
190+
$is_values_naming_supported
191+
? 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
192+
: 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
171193
'REPLACE INTO t (c) VALUES (1)'
172194
);
173195

174196
$this->assertQuery(
175-
'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true',
197+
$is_values_naming_supported
198+
? 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 )) WHERE true'
199+
: 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
176200
'REPLACE INTO wp.t (c) VALUES (1)'
177201
);
178202

179203
$this->assertQuery(
180-
'REPLACE INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true',
204+
$is_values_naming_supported
205+
? 'REPLACE INTO `t` (`c1`, `c2`) SELECT `column1`, `column2` FROM (VALUES ( 1 , 2 )) WHERE true'
206+
: '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',
181207
'REPLACE INTO t (c1, c2) VALUES (1, 2)'
182208
);
183209

184210
$this->assertQuery(
185-
'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
211+
$is_values_naming_supported
212+
? 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
213+
: 'REPLACE INTO `t` (`c`) SELECT `column1` FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
186214
'REPLACE INTO t (c) VALUES (1), (2)'
187215
);
188216

@@ -200,18 +228,26 @@ public function testReplaceWithTypeCasting(): void {
200228
$this->driver->query( 'CREATE TABLE t2 (c1 TEXT, c2 TEXT)' );
201229
$this->driver->query( 'INSERT INTO t2 VALUES (1, 2)' );
202230

231+
$is_values_naming_supported = version_compare( $this->driver->get_sqlite_version(), '3.33.0', '>=' );
232+
203233
$this->assertQuery(
204-
'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true',
234+
$is_values_naming_supported
235+
? 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 )) WHERE true'
236+
: 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 )) WHERE true',
205237
'REPLACE INTO t1 (c1) VALUES (1)'
206238
);
207239

208240
$this->assertQuery(
209-
'REPLACE INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true',
241+
$is_values_naming_supported
242+
? 'REPLACE INTO `t1` (`c1`, `c2`) SELECT CAST(`column1` AS TEXT), CAST(`column2` AS TEXT) FROM (VALUES ( 1 , 2 )) WHERE true'
243+
: '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',
210244
'REPLACE INTO t1 (c1, c2) VALUES (1, 2)'
211245
);
212246

213247
$this->assertQuery(
214-
'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true',
248+
$is_values_naming_supported
249+
? 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (VALUES ( 1 ) , ( 2 )) WHERE true'
250+
: 'REPLACE INTO `t1` (`c1`) SELECT CAST(`column1` AS TEXT) FROM (SELECT NULL AS `column1` WHERE FALSE UNION ALL VALUES ( 1 ) , ( 2 )) WHERE true',
215251
'REPLACE INTO t1 (c1) VALUES (1), (2)'
216252
);
217253

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)