diff --git a/config.js b/config.js index dc6b3b8..b8f60b8 100644 --- a/config.js +++ b/config.js @@ -13,7 +13,7 @@ module.exports = { 'timestamp': 'TIMESTAMP', 'timestamptz': 'TIMESTAMP', // PostgreSQL timestamp with time zone 'bool': 'BIT', - 'text': 'TEXT', + 'text': 'VARCHAR(8000)', // 达梦的TEXT是CLOB不能建索引,改用VARCHAR 'bytea': 'BLOB', 'float8': 'DOUBLE', // PostgreSQL double precision 'float4': 'REAL', // PostgreSQL real/float diff --git a/converter.js b/converter.js index 817ede2..75d09f0 100644 --- a/converter.js +++ b/converter.js @@ -44,7 +44,7 @@ class PG2DMConverter { let converted = sql; // 1. 转换基本类型(包括浮点类型和时间戳类型) - const typePattern = /\b(int8|int4|int2|numeric|bool|float8|float4|float|timestamptz)\b/gi; + const typePattern = /\b(int8|int4|int2|numeric|bool|float8|float4|float|timestamptz|text)\b/gi; converted = converted.replace(typePattern, (match) => { const lowerMatch = match.toLowerCase(); @@ -63,6 +63,27 @@ class PG2DMConverter { return `TIMESTAMP`; }); + // 3. 移除时区子句 + // PostgreSQL: TIMESTAMP without time zone / TIMESTAMP with time zone + // 达梦: TIMESTAMP (不支持时区子句) + converted = converted.replace(/TIMESTAMP\s+(without|with)\s+time\s+zone/gi, 'TIMESTAMP'); + const timezoneMatches = sql.match(/TIMESTAMP\s+(without|with)\s+time\s+zone/gi); + if (timezoneMatches) { + this.log(`移除 ${timezoneMatches.length} 个TIMESTAMP时区子句`); + } + + // 4. 修正DECIMAL精度超出范围 + // 达梦DECIMAL最大精度38位,PostgreSQL可以到1000位 + converted = converted.replace(/DECIMAL\s*\((\d+)\s*,\s*(\d+)\)/gi, (match, precision, scale) => { + const p = parseInt(precision); + const s = parseInt(scale); + if (p > 38) { + this.warn(`DECIMAL(${p},${s}) 精度超出达梦限制(最大38),已调整为DECIMAL(38,${s})`); + return `DECIMAL(38,${s})`; + } + return match; + }); + return converted; } @@ -177,17 +198,14 @@ class PG2DMConverter { removeTypeQuotes(sql) { let converted = sql; - // 先统一小写的text类型为TEXT(避免和"text_ops"操作符混淆) - converted = converted.replace(/\s+text\s+/gi, ' TEXT '); - converted = converted.replace(/\s+text([,\n\r])/gi, ' TEXT$1'); - // 移除引号中的数据类型(达梦不需要给类型加引号) - // 必须在独立的步骤中处理,确保不会误伤列名 - // 匹配模式:前面有空格,后面有空格或逗号 - converted = converted.replace(/\s"(BIGINT|INT|SMALLINT|TINYINT|DECIMAL|NUMERIC|VARCHAR|CHAR|TEXT|DATE|TIME|TIMESTAMP|BIT|BOOLEAN|BOOL|BLOB|CLOB)"\s/gi, ' $1 '); + // 1. 先处理带括号的类型:VARCHAR(8000), DECIMAL(20,6)等 + converted = converted.replace(/\s"(VARCHAR|CHAR|DECIMAL|NUMERIC)\s*\([^)]+\)"\s/gi, ' $1 '); + converted = converted.replace(/\s"(VARCHAR|CHAR|DECIMAL|NUMERIC)\s*\([^)]+\)"([,\n\r])/gi, ' $1$2'); - // 处理行尾的类型(后面紧跟换行或逗号) - converted = converted.replace(/\s"(BIGINT|INT|SMALLINT|TINYINT|DECIMAL|NUMERIC|VARCHAR|CHAR|TEXT|DATE|TIME|TIMESTAMP|BIT|BOOLEAN|BOOL|BLOB|CLOB)"([,\n\r])/gi, ' $1$2'); + // 2. 再处理简单类型 + converted = converted.replace(/\s"(BIGINT|INT|SMALLINT|TINYINT|VARCHAR|CHAR|TEXT|DATE|TIME|TIMESTAMP|BIT|BOOLEAN|BOOL|BLOB|CLOB)"\s/gi, ' $1 '); + converted = converted.replace(/\s"(BIGINT|INT|SMALLINT|TINYINT|VARCHAR|CHAR|TEXT|DATE|TIME|TIMESTAMP|BIT|BOOLEAN|BOOL|BLOB|CLOB)"([,\n\r])/gi, ' $1$2'); this.log('移除数据类型引号');