Add MariaDB queue table. Fix Bugs
This commit is contained in:
+2
-1
@@ -1,6 +1,7 @@
|
||||
import isVectorField from "./is-vector-field";
|
||||
import mapDataType from "./map-data-types";
|
||||
import MariaDBQuoteGen from "./mariadb-quote-gen";
|
||||
import { escapeStringLiteral } from "../../utils/sql-identifier";
|
||||
export default function buildColumnDefinition(field, options = {}) {
|
||||
if (!field.fieldName) {
|
||||
throw new Error("Field name is required");
|
||||
@@ -27,7 +28,7 @@ export default function buildColumnDefinition(field, options = {}) {
|
||||
}
|
||||
if (field.defaultValue !== undefined) {
|
||||
if (typeof field.defaultValue === "string") {
|
||||
parts.push(`DEFAULT '${field.defaultValue.replace(/'/g, "''")}'`);
|
||||
parts.push(`DEFAULT '${escapeStringLiteral(field.defaultValue)}'`);
|
||||
}
|
||||
else {
|
||||
parts.push(`DEFAULT ${field.defaultValue}`);
|
||||
|
||||
Vendored
+3
-2
@@ -1,3 +1,4 @@
|
||||
import { escapeStringLiteral } from "../../utils/sql-identifier";
|
||||
export default function mapDataType(field) {
|
||||
const dataType = field.dataType?.toUpperCase() || "TEXT";
|
||||
const vectorSize = field.vectorSize || 1536;
|
||||
@@ -76,13 +77,13 @@ export default function mapDataType(field) {
|
||||
return "TINYINT(1)";
|
||||
case "ENUM": {
|
||||
const enumVals = (field.options || [])
|
||||
.map((v) => `'${String(v).replace(/'/g, "''")}'`)
|
||||
.map((v) => `'${escapeStringLiteral(String(v))}'`)
|
||||
.join(", ");
|
||||
return `ENUM(${enumVals || "''"})`;
|
||||
}
|
||||
case "SET": {
|
||||
const setVals = (field.options || [])
|
||||
.map((v) => `'${String(v).replace(/'/g, "''")}'`)
|
||||
.map((v) => `'${escapeStringLiteral(String(v))}'`)
|
||||
.join(", ");
|
||||
return `SET(${setVals || "''"})`;
|
||||
}
|
||||
|
||||
Vendored
+88
-30
@@ -12,8 +12,8 @@ import { dropForeignKeysOnColumns } from "./sync-foreign-keys";
|
||||
* Live types often include display widths (e.g. bigint(20) vs BIGINT).
|
||||
*/
|
||||
function columnTypesMatch(liveType, expectedType) {
|
||||
const live = liveType.toLowerCase().replace(/\s+/g, "");
|
||||
const expected = expectedType.toLowerCase().replace(/\s+/g, "");
|
||||
const live = normalizeSqlOutsideQuotes(liveType, "");
|
||||
const expected = normalizeSqlOutsideQuotes(expectedType, "");
|
||||
if (live === expected)
|
||||
return true;
|
||||
// live may include display width: bigint(20) vs bigint
|
||||
@@ -41,25 +41,71 @@ function vectorTypeDiverged(liveType, liveComment, field) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function normalizeDefault(value) {
|
||||
/**
|
||||
* Lowercase SQL and collapse whitespace to `space`, leaving the contents of
|
||||
* single-quoted literals untouched (ENUM options, string defaults).
|
||||
*/
|
||||
function normalizeSqlOutsideQuotes(sql, space) {
|
||||
return sql
|
||||
.trim()
|
||||
.split(/('(?:[^'\\]|''|\\.)*')/)
|
||||
.map((part, i) => i % 2 === 1 ? part : part.toLowerCase().replace(/\s+/g, space))
|
||||
.join("");
|
||||
}
|
||||
/**
|
||||
* Parse a default as MariaDB reports it in COLUMN_DEFAULT (or as written in
|
||||
* `defaultValueLiteral`): unquoted NULL, an escaped quoted string literal,
|
||||
* or an expression / number.
|
||||
*/
|
||||
function parseDefault(value) {
|
||||
if (value == null)
|
||||
return "";
|
||||
let v = String(value).trim();
|
||||
// Strip surrounding quotes MariaDB may include
|
||||
if ((v.startsWith("'") && v.endsWith("'")) ||
|
||||
(v.startsWith('"') && v.endsWith('"'))) {
|
||||
v = v.slice(1, -1);
|
||||
return { kind: "none" };
|
||||
const v = String(value).trim();
|
||||
if (/^null$/i.test(v))
|
||||
return { kind: "none" };
|
||||
if (/^'(?:[^'\\]|''|\\.)*'$/s.test(v)) {
|
||||
const escapes = {
|
||||
n: "\n",
|
||||
t: "\t",
|
||||
r: "\r",
|
||||
"0": "\0",
|
||||
};
|
||||
return {
|
||||
kind: "string",
|
||||
value: v
|
||||
.slice(1, -1)
|
||||
.replace(/''|\\(.)/gs, (m, ch) => m === "''" ? "'" : (escapes[ch] ?? ch)),
|
||||
};
|
||||
}
|
||||
return v.toLowerCase().replace(/\s+/g, " ");
|
||||
let expr = normalizeSqlOutsideQuotes(v, "");
|
||||
// MariaDB may wrap expressions in parentheses
|
||||
while (/^\(.*\)$/.test(expr) && balancedParens(expr.slice(1, -1))) {
|
||||
expr = expr.slice(1, -1);
|
||||
}
|
||||
return { kind: "expr", value: expr };
|
||||
}
|
||||
function balancedParens(s) {
|
||||
let depth = 0;
|
||||
for (const ch of s) {
|
||||
if (ch === "(")
|
||||
depth++;
|
||||
if (ch === ")" && --depth < 0)
|
||||
return false;
|
||||
}
|
||||
return depth === 0;
|
||||
}
|
||||
function expectedDefault(field) {
|
||||
if (field.defaultValue !== undefined) {
|
||||
return String(field.defaultValue);
|
||||
// Strings are emitted as quoted literals; numbers verbatim
|
||||
if (typeof field.defaultValue === "string") {
|
||||
return { kind: "string", value: field.defaultValue };
|
||||
}
|
||||
return parseDefault(String(field.defaultValue));
|
||||
}
|
||||
if (field.defaultValueLiteral) {
|
||||
return field.defaultValueLiteral;
|
||||
return parseDefault(field.defaultValueLiteral);
|
||||
}
|
||||
return null;
|
||||
return { kind: "none" };
|
||||
}
|
||||
function expectedOnUpdate(field) {
|
||||
if (field.onUpdate)
|
||||
@@ -68,15 +114,29 @@ function expectedOnUpdate(field) {
|
||||
return field.onUpdateLiteral;
|
||||
return null;
|
||||
}
|
||||
function defaultsMatch(liveDefault, expected) {
|
||||
if (liveDefault === expected)
|
||||
return true;
|
||||
if (expected.includes("current_timestamp") &&
|
||||
liveDefault.includes("current_timestamp")) {
|
||||
return true;
|
||||
function defaultsMatch(live, expected) {
|
||||
if (live.kind === "none" || expected.kind === "none") {
|
||||
return live.kind === expected.kind;
|
||||
}
|
||||
if (live.value === expected.value) {
|
||||
// A string never equals an expression of the same text, except for
|
||||
// numbers, which MariaDB reports unquoted on numeric columns
|
||||
return live.kind === expected.kind || isNumeric(live.value);
|
||||
}
|
||||
// DECIMAL defaults are reported padded: 1.5 → 1.50
|
||||
if (isNumeric(live.value) && isNumeric(expected.value)) {
|
||||
return Number(live.value) === Number(expected.value);
|
||||
}
|
||||
if (live.kind === "expr" && expected.kind === "expr") {
|
||||
// now() / current_timestamp / current_timestamp(N) are aliases
|
||||
const ts = /^(current_timestamp|now|localtimestamp|localtime)(\(\d*\))?$/;
|
||||
return ts.test(live.value) && ts.test(expected.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isNumeric(value) {
|
||||
return value.trim() !== "" && Number.isFinite(Number(value));
|
||||
}
|
||||
function columnAttributesDiverged(live, field) {
|
||||
const wantsNotNull = fieldRequiresNotNull(field);
|
||||
if (wantsNotNull && live.isNullable) {
|
||||
@@ -92,12 +152,12 @@ function columnAttributesDiverged(live, field) {
|
||||
return true;
|
||||
}
|
||||
const wantsDefault = expectedDefault(field);
|
||||
if (wantsDefault != null) {
|
||||
const liveDefault = normalizeDefault(live.columnDefault);
|
||||
const expected = normalizeDefault(wantsDefault);
|
||||
if (!defaultsMatch(liveDefault, expected)) {
|
||||
return true;
|
||||
}
|
||||
const liveDefault = parseDefault(live.columnDefault);
|
||||
// Older servers give TIMESTAMP columns an implicit default
|
||||
const implicitDefault = wantsDefault.kind === "none" &&
|
||||
field.dataType?.toUpperCase() === "TIMESTAMP";
|
||||
if (!implicitDefault && !defaultsMatch(liveDefault, wantsDefault)) {
|
||||
return true;
|
||||
}
|
||||
const wantsOnUpdate = expectedOnUpdate(field);
|
||||
const hasOnUpdate = /on update/i.test(live.extra || "");
|
||||
@@ -108,11 +168,9 @@ function columnAttributesDiverged(live, field) {
|
||||
return true;
|
||||
}
|
||||
if (wantsOnUpdate && hasOnUpdate) {
|
||||
const liveOnUpdate = normalizeDefault((live.extra.match(/on update\s+(.+)/i) || [])[1] || "");
|
||||
const expectedOu = normalizeDefault(wantsOnUpdate);
|
||||
if (liveOnUpdate &&
|
||||
expectedOu &&
|
||||
!defaultsMatch(liveOnUpdate, expectedOu)) {
|
||||
const liveOnUpdate = parseDefault((live.extra.match(/on update\s+(.+)/i) || [])[1] || "");
|
||||
const expectedOu = parseDefault(wantsOnUpdate);
|
||||
if (!defaultsMatch(liveOnUpdate, expectedOu)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user