Fix domain column from TEXT to VARCHAR(255) for MySQL compatibility
Co-authored-by: CommanderStorm <26258709+CommanderStorm@users.noreply.github.com>
This commit is contained in:
parent
1f5d184b11
commit
17885b5438
@ -6,7 +6,9 @@ exports.up = function (knex) {
|
||||
.createTable("domain_expiry", (table) => {
|
||||
table.increments("id");
|
||||
table.datetime("last_check");
|
||||
table.text("domain").unique().notNullable();
|
||||
// Use VARCHAR(255) for MySQL/MariaDB compatibility with unique constraint
|
||||
// Maximum domain name length is 253 characters (255 octets on the wire)
|
||||
table.string("domain", 255).unique().notNullable();
|
||||
table.datetime("expiry");
|
||||
table.integer("last_expiry_notification_sent").defaultTo(null);
|
||||
});
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
// Fix domain column type from TEXT to VARCHAR(255) for MySQL/MariaDB compatibility
|
||||
// TEXT columns cannot have UNIQUE constraints in MySQL 8.0 and older MariaDB versions
|
||||
// Maximum domain name length is 253 characters (255 octets on the wire)
|
||||
exports.up = async function (knex) {
|
||||
const isSQLite = knex.client.dialect === "sqlite3";
|
||||
|
||||
if (isSQLite) {
|
||||
// For SQLite, we need to recreate the table since ALTER COLUMN is limited
|
||||
// Check if the column type needs to be changed by checking if it's currently TEXT
|
||||
const tableInfo = await knex.raw("PRAGMA table_info('domain_expiry')");
|
||||
const domainColumn = tableInfo.find(col => col.name === "domain");
|
||||
|
||||
if (domainColumn && domainColumn.type.toUpperCase() === "TEXT") {
|
||||
// Create new table with correct column type
|
||||
await knex.schema.createTable("domain_expiry_new", (table) => {
|
||||
table.increments("id");
|
||||
table.datetime("last_check");
|
||||
table.string("domain", 255).unique().notNullable();
|
||||
table.datetime("expiry");
|
||||
table.integer("last_expiry_notification_sent").defaultTo(null);
|
||||
});
|
||||
|
||||
// Copy data from old table to new table
|
||||
await knex.raw(`
|
||||
INSERT INTO domain_expiry_new (id, last_check, domain, expiry, last_expiry_notification_sent)
|
||||
SELECT id, last_check, domain, expiry, last_expiry_notification_sent
|
||||
FROM domain_expiry
|
||||
`);
|
||||
|
||||
// Drop old table and rename new table
|
||||
await knex.schema.dropTable("domain_expiry");
|
||||
await knex.schema.renameTable("domain_expiry_new", "domain_expiry");
|
||||
}
|
||||
} else {
|
||||
// For MySQL/MariaDB
|
||||
// Check if column is TEXT type and alter to VARCHAR(255) if needed
|
||||
const dbName = knex.client.database();
|
||||
const columnInfo = await knex.raw(`
|
||||
SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'domain_expiry' AND COLUMN_NAME = 'domain'
|
||||
`, [ dbName ]);
|
||||
|
||||
const dataType = columnInfo[0]?.[0]?.DATA_TYPE?.toUpperCase();
|
||||
|
||||
if (dataType === "TEXT") {
|
||||
// Drop the unique constraint first (if it exists)
|
||||
try {
|
||||
await knex.raw("ALTER TABLE domain_expiry DROP INDEX domain_expiry_domain_unique");
|
||||
} catch (e) {
|
||||
// Index might not exist, ignore error
|
||||
}
|
||||
|
||||
// Alter column type to VARCHAR(255)
|
||||
await knex.schema.alterTable("domain_expiry", function (table) {
|
||||
table.string("domain", 255).notNullable().alter();
|
||||
});
|
||||
|
||||
// Re-add unique constraint
|
||||
await knex.schema.alterTable("domain_expiry", function (table) {
|
||||
table.unique("domain");
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.down = async function (knex) {
|
||||
// No rollback needed - keeping VARCHAR(255) is the correct state
|
||||
// Rolling back to TEXT would cause issues with unique constraints
|
||||
};
|
||||
@ -130,4 +130,79 @@ describe("Database Migration", () => {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
"MySQL migrations run successfully from fresh database",
|
||||
{
|
||||
skip:
|
||||
!!process.env.CI &&
|
||||
(process.platform !== "linux" || process.arch !== "x64"),
|
||||
},
|
||||
async () => {
|
||||
// Start MySQL 8.0 container (the version mentioned in the issue)
|
||||
const mysqlContainer = await new GenericContainer("mysql:8.0")
|
||||
.withEnvironment({
|
||||
"MYSQL_ROOT_PASSWORD": "root",
|
||||
"MYSQL_DATABASE": "kuma_test",
|
||||
"MYSQL_USER": "kuma",
|
||||
"MYSQL_PASSWORD": "kuma"
|
||||
})
|
||||
.withExposedPorts(3306)
|
||||
.withWaitStrategy(Wait.forLogMessage("/usr/sbin/mysqld: ready for connections", 2))
|
||||
.withStartupTimeout(120000)
|
||||
.start();
|
||||
|
||||
// Wait a bit more to ensure MySQL is fully ready
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
const knex = require("knex");
|
||||
const knexInstance = knex({
|
||||
client: "mysql2",
|
||||
connection: {
|
||||
host: mysqlContainer.getHost(),
|
||||
port: mysqlContainer.getMappedPort(3306),
|
||||
user: "kuma",
|
||||
password: "kuma",
|
||||
database: "kuma_test",
|
||||
connectTimeout: 60000,
|
||||
},
|
||||
pool: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
acquireTimeoutMillis: 60000,
|
||||
idleTimeoutMillis: 60000,
|
||||
},
|
||||
});
|
||||
|
||||
// Setup R (redbean) with knex instance like production code does
|
||||
const { R } = require("redbean-node");
|
||||
R.setup(knexInstance);
|
||||
|
||||
try {
|
||||
// Use production code to initialize MySQL tables
|
||||
const { createTables } = require("../../db/knex_init_db.js");
|
||||
await createTables();
|
||||
|
||||
// Run all migrations like production code does
|
||||
await R.knex.migrate.latest({
|
||||
directory: path.join(__dirname, "../../db/knex_migrations")
|
||||
});
|
||||
|
||||
// Test passes if migrations complete successfully without errors
|
||||
|
||||
} finally {
|
||||
// Clean up
|
||||
try {
|
||||
await R.knex.destroy();
|
||||
} catch (e) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
try {
|
||||
await mysqlContainer.stop();
|
||||
} catch (e) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user