Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Core/DBI.php
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,11 @@ private static function buildFieldDefinition($name, $attrs, $tableName) {
// Default value
if (array_key_exists('default', $attrs)) {
$field .= ' DEFAULT ';
$field .= (is_string($attrs['default']) ? "'" . $attrs['default'] . "'" : $attrs['default']);
if (is_null($attrs['default'])) {
$field .= 'NULL';
} else {
$field .= (is_string($attrs['default']) ? "'" . $attrs['default'] . "'" : $attrs['default']);
}
}

// Required/NOT NULL
Expand Down
57 changes: 57 additions & 0 deletions tests/DatabaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,61 @@ public function testDatabaseCreationAndSwitch() {
$password = null;
$this->assertTrue(\Kyte\Core\DBI::createDatabase("TestDatabase2", "TestUser2", $password, true));
}

/**
* Regression test: createTable must accept fields declared with
* 'default' => null and emit DEFAULT NULL in the generated SQL.
*
* Before this was fixed, buildFieldDefinition's default-value branch fell
* through to a fall-through concat that coerced null to '', producing
* broken SQL like `language varchar(5) DEFAULT NOT NULL,`. The Application
* model's `language` field has 'default' => null, so any fresh createTable
* of it (e.g. during install) blew up. gust/lib/Database.php still has an
* identical copy of this bug and should be patched when gust is next
* touched (see design doc section 11, gust evaluation).
*/
public function testCreateTableAcceptsNullDefault() {
// The preceding createDatabase tests leave the mysqli connection
// without an active database context. Re-select kytedev explicitly.
// dbInit won't help here because connect() early-returns once a
// connection object already exists.
\Kyte\Core\DBI::query('USE `' . KYTE_DB_DATABASE . '`');

$tblName = 'NullDefaultProbe_' . substr(bin2hex(random_bytes(4)), 0, 8);

$modelDef = [
'name' => $tblName,
'struct' => [
'id' => [
'type' => 'i',
'required' => true,
'pk' => true,
'size' => 11,
'date' => false,
],
'optional_label' => [
'type' => 's',
'required' => false,
'size' => 64,
'date' => false,
'default' => null,
],
],
];

try {
$this->assertTrue(\Kyte\Core\DBI::createTable($modelDef));

$rows = \Kyte\Core\DBI::query("SHOW CREATE TABLE `$tblName`");
$this->assertNotEmpty($rows);
$createSql = $rows[0]['Create Table'] ?? '';
$this->assertStringContainsStringIgnoringCase(
'DEFAULT NULL',
$createSql,
"Column with 'default' => null should surface as DEFAULT NULL in SHOW CREATE TABLE output."
);
} finally {
\Kyte\Core\DBI::query("DROP TABLE IF EXISTS `$tblName`");
}
}
}
Loading