| @@ -73,11 +73,21 @@ | ||
| 73 | 73 | * |
| 74 | 74 | * @var bool |
| 75 | 75 | * @since 0.0.1 |
| 76 | 76 | */ |
| 77 | - private $db_upgradable; | |
| 77 | + protected $db_upgradable; | |
| 78 | 78 | |
| 79 | 79 | /** |
| 80 | + * Previously stored version of this table before the current upgrade | |
| 81 | + * (0 when the table had no recorded version yet). Exposed so child classes | |
| 82 | + * can gate one-time data migrations in run_data_migrations(). | |
| 83 | + * | |
| 84 | + * @var int | |
| 85 | + * @since 1.3.0 | |
| 86 | + */ | |
| 87 | + protected $prev_version = 0; | |
| 88 | + | |
| 89 | + /** | |
| 80 | 90 | * Current table database result caches. |
| 81 | 91 | * |
| 82 | 92 | * @var array<mixed> |
| 83 | 93 | * @since 0.0.1 |
| @@ -136,8 +146,18 @@ | ||
| 136 | 146 | return []; |
| 137 | 147 | } |
| 138 | 148 | |
| 139 | 149 | /** |
| 150 | + * Run one-time data migrations for this table after its columns are in | |
| 151 | + * place. Called only while the table is upgradable (see register.php). | |
| 152 | + * No-op by default; override in a child class and gate on $this->prev_version. | |
| 153 | + * | |
| 154 | + * @return void | |
| 155 | + * @since 1.3.0 | |
| 156 | + */ | |
| 157 | + public function run_data_migrations() {} | |
| 158 | + | |
| 159 | + /** | |
| 140 | 160 | * Start the database upgrade process. |
| 141 | 161 | * |
| 142 | 162 | * @return void |
| 143 | 163 | * @since 0.0.1 |
| @@ -146,8 +166,10 @@ | ||
| 146 | 166 | $versions = Helper::get_suredonation_option( self::VERSION_OPTION_KEY, [] ); |
| 147 | 167 | $versions = is_array( $versions ) ? $versions : []; |
| 148 | 168 | $prev_version = ! empty( $versions[ $this->table_suffix ] ) ? absint( $versions[ $this->table_suffix ] ) : false; |
| 149 | 169 | |
| 170 | + $this->prev_version = $prev_version ? (int) $prev_version : 0; | |
| 171 | + | |
| 150 | 172 | if ( ! $prev_version ) { |
| 151 | 173 | $this->db_upgradable = true; |
| 152 | 174 | return; |
| 153 | 175 | } |
| @@ -240,8 +262,13 @@ | ||
| 240 | 262 | // the interpolated column-definition string, turning literal `DEFAULT ''` |
| 241 | 263 | // into `DEFAULT \'\'`, which MySQL rejects with a syntax error. The |
| 242 | 264 | // table name, column list, and charset are all hardcoded DDL (not user |
| 243 | 265 | // input), so direct concatenation is safe. |
| 266 | + // | |
| 267 | + // Column definitions must quote string literals with single quotes. | |
| 268 | + // wpdb strips the composite `ANSI` sql_mode on connect but not a | |
| 269 | + // standalone `ANSI_QUOTES`, under which `DEFAULT ""` parses as an empty | |
| 270 | + // identifier and fails the statement permanently on every retry. | |
| 244 | 271 | $query = sprintf( |
| 245 | 272 | 'CREATE TABLE IF NOT EXISTS `%s` ( %s ) %s', |
| 246 | 273 | esc_sql( $this->get_tablename() ), |
| 247 | 274 | $columns_list, |