diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e2ea49..a9f0756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased][unreleased] +- Add SQL array type support in createEntity + ## [3.0.0-alpha.1][] - 2023-05-27 - Update metadomain to 2.0.0-alpha.1 diff --git a/lib/pg.js b/lib/pg.js index 2af7051..ae40a12 100644 --- a/lib/pg.js +++ b/lib/pg.js @@ -17,6 +17,8 @@ const PG_TYPES = { bigint: { metadata: { pg: 'bigint' } }, enum: { metadata: { pg: 'varchar' } }, json: { metadata: { pg: 'jsonb' } }, + array: { metadata: { pg: 'array' } }, + set: { metadata: { pg: 'array' } }, real: { js: 'number', metadata: { pg: 'real' } }, double: { js: 'number', metadata: { pg: 'double precision' } }, @@ -154,37 +156,51 @@ const createEntity = (model, name) => { const fields = Object.keys(flat); for (const field of fields) { const def = flat[field]; - if (ignoreField(name, field, def)) continue; - const nullable = def.required ? ' NOT NULL' : ' NULL'; - if (def.type) { - const kind = isFirstUpper(def.type) ? DB_RELATION : DB_FIELD; - const { pg } = def.constructor.metadata; - let pgType = kind === DB_FIELD ? pg : 'bigint'; - if (!pgType) { - throw new Error(`Unknown type: ${def.type} in ${name}.${field}`); - } - const pgField = field + (kind === DB_FIELD ? '' : 'Id'); - let defVal = ''; - if (typeof def.default !== 'undefined') { - defVal = ' DEFAULT '; - if (def.type === 'datetime' && def.default === 'now') { - defVal += `CURRENT_TIMESTAMP`; - } else { - const quoted = def.type === 'number' || def.type === 'boolean'; - defVal += quoted ? def.default : `'${def.default}'`; - } - } - if (def.length?.max) pgType = `${pgType}(${def.length.max})`; - if (def.index || def.unique) { - idx.push(createIndex(name, field, def, entity)); + if (ignoreField(name, field, def) || !def.type) continue; + const kind = isFirstUpper(def.type) ? DB_RELATION : DB_FIELD; + const { pg } = def.constructor.metadata; + let pgType = kind === DB_FIELD ? pg : 'bigint'; + if (!pgType) { + throw new Error(`Unknown type: ${def.type} in ${name}.${field}`); + } + const pgField = field + (kind === DB_FIELD ? '' : 'Id'); + let defVal = ''; + if (typeof def.default !== 'undefined') { + defVal = ' DEFAULT '; + if (def.type === 'datetime' && def.default === 'now') { + defVal += `CURRENT_TIMESTAMP`; + } else { + const quoted = def.type === 'number' || def.type === 'boolean'; + defVal += quoted ? def.default : `'${def.default}'`; } - sql.push(` "${pgField}" ${pgType}${nullable}${defVal},`); - if (kind === DB_RELATION) { - const ref = model.entities.get(def.type); - if (!ref) throw new Error(`Unknown schema: ${def.type}`); - const refId = ref.kind === 'registry'; - idx.push(foreignKey(name, field, def, refId)); + } + const maxLength = def.length?.max; + if (pgType.toLowerCase() === 'array') { + const { pg: pgArrayType } = def.value?.constructor.metadata || null; + if (!pgArrayType) + throw new Error( + `Unknown array type: ${def.value?.type} in ${name}.${field}`, + ); + const isInt = (num, grThn = 0) => Number.isInteger(num) && num > grThn; + const dims = def.dims || (maxLength ? [maxLength] : undefined); + pgType = `${pgArrayType}`; + pgType += dims?.length > 1 || isInt(dims, 1) ? '' : ' ARRAY'; + if (Array.isArray(dims)) { + pgType += dims.map((dim) => `[${isInt(dim) ? dim : ''}]`).join(''); + } else if (isInt(dims)) { + pgType += Array(dims).fill('[]').join(''); } + } else if (maxLength) pgType = `${pgType}(${maxLength})`; + if (def.index || def.unique) { + idx.push(createIndex(name, field, def, entity)); + } + const nullable = def.required ? ' NOT NULL' : ' NULL'; + sql.push(` "${pgField}" ${pgType}${nullable}${defVal},`); + if (kind === DB_RELATION) { + const ref = model.entities.get(def.type); + if (!ref) throw new Error(`Unknown schema: ${def.type}`); + const refId = ref.kind === 'registry'; + idx.push(foreignKey(name, field, def, refId)); } } const indexes = Object.keys(entity.indexes);