Skip to content

Commit 4c63355

Browse files
authored
Add handling for adding/modifying columns with FIRST/AFTER (#147)
Add handling for adding/modifying columns with FIRST/AFTER, such as: ```sql ALTER TABLE _tmp_table ADD COLUMN new_first_column VARCHAR(255) NOT NULL DEFAULT '' FIRST; ALTER TABLE _tmp_table ADD COLUMN new_column VARCHAR(255) NOT NULL DEFAULT '' AFTER id; ALTER TABLE _tmp_table CHANGE id id int(11) NOT NULL FIRST; ALTER TABLE _tmp_table CHANGE id id int(11) NOT NULL AFTER name; ``` SQLite doesn't support FIRST/AFTER, so this implementation simply ignores these. Previously, for ADD COLUMN statements, it ended up with `SQLSTATE[HY000]: General error: 1 near "FIRST": syntax error.` and `SQLSTATE[HY000]: General error: 1 near "AFTER": syntax error.`; for CHANGE statements, this was already ignored and I only added a test.
1 parent dea3efd commit 4c63355

File tree

2 files changed

+302
-0
lines changed

2 files changed

+302
-0
lines changed

tests/WP_SQLite_Translator_Tests.php

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,279 @@ public function testAlterTableAddNotNullVarcharColumn() {
10271027
);
10281028
}
10291029

1030+
public function testAlterTableWithColumnFirstAndAfter() {
1031+
$this->assertQuery(
1032+
"CREATE TABLE _tmp_table (
1033+
id int(11) NOT NULL,
1034+
name varchar(20) NOT NULL default ''
1035+
);"
1036+
);
1037+
1038+
// ADD COLUMN with FIRST
1039+
$this->assertQuery(
1040+
"ALTER TABLE _tmp_table ADD COLUMN new_first_column VARCHAR(255) NOT NULL DEFAULT '' FIRST"
1041+
);
1042+
$results = $this->assertQuery( 'DESCRIBE _tmp_table;' );
1043+
$this->assertEquals(
1044+
array(
1045+
(object) array(
1046+
'Field' => 'id',
1047+
'Type' => 'int(11)',
1048+
'Null' => 'NO',
1049+
'Key' => '',
1050+
'Default' => '0',
1051+
'Extra' => '',
1052+
),
1053+
(object) array(
1054+
'Field' => 'name',
1055+
'Type' => 'varchar(20)',
1056+
'Null' => 'NO',
1057+
'Key' => '',
1058+
'Default' => null,
1059+
'Extra' => '',
1060+
),
1061+
(object) array(
1062+
'Field' => 'new_first_column',
1063+
'Type' => 'varchar(255)',
1064+
'Null' => 'NO',
1065+
'Key' => '',
1066+
'Default' => '',
1067+
'Extra' => '',
1068+
),
1069+
),
1070+
$results
1071+
);
1072+
1073+
// ADD COLUMN with AFTER
1074+
$this->assertQuery(
1075+
"ALTER TABLE _tmp_table ADD COLUMN new_column VARCHAR(255) NOT NULL DEFAULT '' AFTER id"
1076+
);
1077+
$results = $this->assertQuery( 'DESCRIBE _tmp_table;' );
1078+
$this->assertEquals(
1079+
array(
1080+
(object) array(
1081+
'Field' => 'id',
1082+
'Type' => 'int(11)',
1083+
'Null' => 'NO',
1084+
'Key' => '',
1085+
'Default' => '0',
1086+
'Extra' => '',
1087+
),
1088+
(object) array(
1089+
'Field' => 'name',
1090+
'Type' => 'varchar(20)',
1091+
'Null' => 'NO',
1092+
'Key' => '',
1093+
'Default' => null,
1094+
'Extra' => '',
1095+
),
1096+
(object) array(
1097+
'Field' => 'new_first_column',
1098+
'Type' => 'varchar(255)',
1099+
'Null' => 'NO',
1100+
'Key' => '',
1101+
'Default' => '',
1102+
'Extra' => '',
1103+
),
1104+
(object) array(
1105+
'Field' => 'new_column',
1106+
'Type' => 'varchar(255)',
1107+
'Null' => 'NO',
1108+
'Key' => '',
1109+
'Default' => '',
1110+
'Extra' => '',
1111+
),
1112+
),
1113+
$results
1114+
);
1115+
1116+
// CHANGE with FIRST
1117+
$this->assertQuery(
1118+
"ALTER TABLE _tmp_table CHANGE id id int(11) NOT NULL DEFAULT '0' FIRST"
1119+
);
1120+
$results = $this->assertQuery( 'DESCRIBE _tmp_table;' );
1121+
$this->assertEquals(
1122+
array(
1123+
(object) array(
1124+
'Field' => 'id',
1125+
'Type' => 'int(11)',
1126+
'Null' => 'NO',
1127+
'Key' => '',
1128+
'Default' => '0',
1129+
'Extra' => '',
1130+
),
1131+
(object) array(
1132+
'Field' => 'name',
1133+
'Type' => 'varchar(20)',
1134+
'Null' => 'NO',
1135+
'Key' => '',
1136+
'Default' => null,
1137+
'Extra' => '',
1138+
),
1139+
(object) array(
1140+
'Field' => 'new_first_column',
1141+
'Type' => 'varchar(255)',
1142+
'Null' => 'NO',
1143+
'Key' => '',
1144+
'Default' => '',
1145+
'Extra' => '',
1146+
),
1147+
(object) array(
1148+
'Field' => 'new_column',
1149+
'Type' => 'varchar(255)',
1150+
'Null' => 'NO',
1151+
'Key' => '',
1152+
'Default' => '',
1153+
'Extra' => '',
1154+
),
1155+
),
1156+
$results
1157+
);
1158+
1159+
// CHANGE with AFTER
1160+
$this->assertQuery(
1161+
"ALTER TABLE _tmp_table CHANGE id id int(11) NOT NULL DEFAULT '0' AFTER name"
1162+
);
1163+
$results = $this->assertQuery( 'DESCRIBE _tmp_table;' );
1164+
$this->assertEquals(
1165+
array(
1166+
(object) array(
1167+
'Field' => 'id',
1168+
'Type' => 'int(11)',
1169+
'Null' => 'NO',
1170+
'Key' => '',
1171+
'Default' => '0',
1172+
'Extra' => '',
1173+
),
1174+
(object) array(
1175+
'Field' => 'name',
1176+
'Type' => 'varchar(20)',
1177+
'Null' => 'NO',
1178+
'Key' => '',
1179+
'Default' => null,
1180+
'Extra' => '',
1181+
),
1182+
(object) array(
1183+
'Field' => 'new_first_column',
1184+
'Type' => 'varchar(255)',
1185+
'Null' => 'NO',
1186+
'Key' => '',
1187+
'Default' => '',
1188+
'Extra' => '',
1189+
),
1190+
(object) array(
1191+
'Field' => 'new_column',
1192+
'Type' => 'varchar(255)',
1193+
'Null' => 'NO',
1194+
'Key' => '',
1195+
'Default' => '',
1196+
'Extra' => '',
1197+
),
1198+
),
1199+
$results
1200+
);
1201+
}
1202+
1203+
public function testAlterTableWithMultiColumnFirstAndAfter() {
1204+
$this->assertQuery(
1205+
'CREATE TABLE _tmp_table (
1206+
id int(11) NOT NULL
1207+
);'
1208+
);
1209+
1210+
// ADD COLUMN
1211+
$this->assertQuery(
1212+
'ALTER TABLE _tmp_table
1213+
ADD COLUMN new1 varchar(255) NOT NULL,
1214+
ADD COLUMN new2 varchar(255) NOT NULL FIRST,
1215+
ADD COLUMN new3 varchar(255) NOT NULL AFTER new1'
1216+
);
1217+
$results = $this->assertQuery( 'DESCRIBE _tmp_table;' );
1218+
$this->assertEquals(
1219+
array(
1220+
(object) array(
1221+
'Field' => 'id',
1222+
'Type' => 'int(11)',
1223+
'Null' => 'NO',
1224+
'Key' => '',
1225+
'Default' => '0',
1226+
'Extra' => '',
1227+
),
1228+
(object) array(
1229+
'Field' => 'new1',
1230+
'Type' => 'varchar(255)',
1231+
'Null' => 'NO',
1232+
'Key' => '',
1233+
'Default' => null,
1234+
'Extra' => '',
1235+
),
1236+
(object) array(
1237+
'Field' => 'new2',
1238+
'Type' => 'varchar(255)',
1239+
'Null' => 'NO',
1240+
'Key' => '',
1241+
'Default' => '',
1242+
'Extra' => '',
1243+
),
1244+
(object) array(
1245+
'Field' => 'new3',
1246+
'Type' => 'varchar(255)',
1247+
'Null' => 'NO',
1248+
'Key' => '',
1249+
'Default' => '',
1250+
'Extra' => '',
1251+
),
1252+
),
1253+
$results
1254+
);
1255+
1256+
// CHANGE
1257+
$this->assertQuery(
1258+
'ALTER TABLE _tmp_table
1259+
CHANGE new1 new1 int(11) NOT NULL FIRST,
1260+
CHANGE new2 new2 int(11) NOT NULL,
1261+
CHANGE new3 new3 int(11) NOT NULL AFTER new2'
1262+
);
1263+
$results = $this->assertQuery( 'DESCRIBE _tmp_table;' );
1264+
$this->assertEquals(
1265+
array(
1266+
(object) array(
1267+
'Field' => 'id',
1268+
'Type' => 'int(11)',
1269+
'Null' => 'NO',
1270+
'Key' => '',
1271+
'Default' => '0',
1272+
'Extra' => '',
1273+
),
1274+
(object) array(
1275+
'Field' => 'new1',
1276+
'Type' => 'int(11)',
1277+
'Null' => 'NO',
1278+
'Key' => '',
1279+
'Default' => null,
1280+
'Extra' => '',
1281+
),
1282+
(object) array(
1283+
'Field' => 'new2',
1284+
'Type' => 'int(11)',
1285+
'Null' => 'NO',
1286+
'Key' => '',
1287+
'Default' => '',
1288+
'Extra' => '',
1289+
),
1290+
(object) array(
1291+
'Field' => 'new3',
1292+
'Type' => 'int(11)',
1293+
'Null' => 'NO',
1294+
'Key' => '',
1295+
'Default' => '',
1296+
'Extra' => '',
1297+
),
1298+
),
1299+
$results
1300+
);
1301+
}
1302+
10301303
public function testAlterTableAddIndex() {
10311304
$result = $this->assertQuery(
10321305
"CREATE TABLE _tmp_table (

wp-includes/sqlite/class-wp-sqlite-translator.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2946,6 +2946,35 @@ private function execute_alter() {
29462946
WP_SQLite_Token::FLAG_KEYWORD_DATA_TYPE
29472947
)
29482948
);
2949+
2950+
// Drop "FIRST" and "AFTER <another-column>", as these are not supported in SQLite.
2951+
$column_position = $this->rewriter->peek(
2952+
array(
2953+
'type' => WP_SQLite_Token::TYPE_KEYWORD,
2954+
'value' => array( 'FIRST', 'AFTER' ),
2955+
)
2956+
);
2957+
2958+
$comma = $this->rewriter->peek(
2959+
array(
2960+
'type' => WP_SQLite_Token::TYPE_OPERATOR,
2961+
'value' => ',',
2962+
)
2963+
);
2964+
2965+
if ( $column_position && ( ! $comma || $column_position->position < $comma->position ) ) {
2966+
$this->rewriter->consume(
2967+
array(
2968+
'type' => WP_SQLite_Token::TYPE_KEYWORD,
2969+
'value' => array( 'FIRST', 'AFTER' ),
2970+
)
2971+
);
2972+
$this->rewriter->drop_last();
2973+
if ( 'AFTER' === strtoupper( $column_position->value ) ) {
2974+
$this->rewriter->skip();
2975+
}
2976+
}
2977+
29492978
$this->update_data_type_cache(
29502979
$this->table_name,
29512980
$column_name,

0 commit comments

Comments
 (0)