@platformatic/sql-mapper 3.63.0-alpha.3 → 3.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +5 -0
- package/index.js +25 -0
- package/lib/entity.js +17 -2
- package/lib/queries/pg.js +26 -8
- package/package.json +5 -5
package/index.d.ts
CHANGED
|
@@ -59,6 +59,11 @@ export interface DBEntityField {
|
|
|
59
59
|
* An option that is true if field is a foreignKey key.
|
|
60
60
|
*/
|
|
61
61
|
foreignKey?: boolean,
|
|
62
|
+
/**
|
|
63
|
+
* An option that is true if a foreign key field is stringified in mapper output
|
|
64
|
+
* because it references a primary key.
|
|
65
|
+
*/
|
|
66
|
+
stringifyOutput?: boolean,
|
|
62
67
|
/**
|
|
63
68
|
* An option that is true if field is nullable.
|
|
64
69
|
*/
|
package/index.js
CHANGED
|
@@ -80,6 +80,29 @@ const defaultAutoTimestampFields = {
|
|
|
80
80
|
updatedAt: 'updated_at'
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function physicalEntityKey (schema, table) {
|
|
84
|
+
return `${schema ?? ''}\0${table}`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function markForeignKeysReferencingPrimaryKeys (entities) {
|
|
88
|
+
const entitiesByTable = new Map()
|
|
89
|
+
|
|
90
|
+
for (const entity of Object.values(entities)) {
|
|
91
|
+
entitiesByTable.set(physicalEntityKey(entity.schema, entity.table), entity)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const entity of Object.values(entities)) {
|
|
95
|
+
for (const relation of entity.relations) {
|
|
96
|
+
const foreignSchema = relation.foreign_table_schema ?? entity.schema
|
|
97
|
+
const foreignEntity = entitiesByTable.get(physicalEntityKey(foreignSchema, relation.foreign_table_name))
|
|
98
|
+
|
|
99
|
+
if (foreignEntity?.primaryKeys.has(relation.foreign_column_name)) {
|
|
100
|
+
entity.fields[relation.column_name].stringifyOutput = true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
83
106
|
async function registerPostgreSQLExtensionTypes (db) {
|
|
84
107
|
try {
|
|
85
108
|
await db.registerTypeParser('vector', parseVector)
|
|
@@ -375,6 +398,8 @@ export async function connect ({
|
|
|
375
398
|
}
|
|
376
399
|
}
|
|
377
400
|
|
|
401
|
+
markForeignKeysReferencingPrimaryKeys(entities)
|
|
402
|
+
|
|
378
403
|
const res = {
|
|
379
404
|
db,
|
|
380
405
|
sql,
|
package/lib/entity.js
CHANGED
|
@@ -104,8 +104,13 @@ function createMapper (
|
|
|
104
104
|
let value = output[key]
|
|
105
105
|
const newKey = fieldMapToRetrieve[key]
|
|
106
106
|
// Do not convert Date objects: they are serialized according to the
|
|
107
|
-
// schema of the field, like non
|
|
108
|
-
if (
|
|
107
|
+
// schema of the field, like other non-stringified columns
|
|
108
|
+
if (
|
|
109
|
+
(primaryKeys.has(key) || fields[key]?.stringifyOutput) &&
|
|
110
|
+
value !== null &&
|
|
111
|
+
value !== undefined &&
|
|
112
|
+
!(value instanceof Date)
|
|
113
|
+
) {
|
|
109
114
|
value = value.toString()
|
|
110
115
|
}
|
|
111
116
|
if (newKey && isVectorType(fields[key].sqlType)) {
|
|
@@ -599,6 +604,16 @@ export function buildEntity (
|
|
|
599
604
|
schemaList?.length > 0 ? schemaList.includes(constraint.foreign_table_schema) : true
|
|
600
605
|
/* istanbul ignore if */
|
|
601
606
|
if (constraint.constraint_type === 'FOREIGN KEY' && isForeignKeySchemaInConfig) {
|
|
607
|
+
/* istanbul ignore next */
|
|
608
|
+
if (!constraint.foreign_table_name) {
|
|
609
|
+
// The referenced side of the foreign key could not be resolved by the introspection
|
|
610
|
+
// query. Ignore the relation rather than failing the whole boot.
|
|
611
|
+
log.warn(
|
|
612
|
+
{ constraint },
|
|
613
|
+
`Could not resolve the table referenced by the foreign key "${constraint.constraint_name}" on "${constraint.table_name}.${constraint.column_name}". The relation will be ignored.`
|
|
614
|
+
)
|
|
615
|
+
continue
|
|
616
|
+
}
|
|
602
617
|
field.foreignKey = true
|
|
603
618
|
const foreignEntityName = singularize(
|
|
604
619
|
camelcase(
|
package/lib/queries/pg.js
CHANGED
|
@@ -80,16 +80,34 @@ export async function listColumns (db, sql, table, schema) {
|
|
|
80
80
|
|
|
81
81
|
export async function listConstraints (db, sql, table, schema) {
|
|
82
82
|
const query = sql`
|
|
83
|
-
SELECT constraints.*, usage.*,
|
|
83
|
+
SELECT constraints.*, usage.*, foreign_table.relname AS foreign_table_name,
|
|
84
|
+
foreign_column.attname AS foreign_column_name, foreign_namespace.nspname AS foreign_table_schema
|
|
84
85
|
FROM information_schema.table_constraints constraints
|
|
85
86
|
JOIN information_schema.key_column_usage usage
|
|
86
|
-
ON constraints.
|
|
87
|
-
AND constraints.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
AND
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
ON constraints.constraint_catalog = usage.constraint_catalog
|
|
88
|
+
AND constraints.constraint_schema = usage.constraint_schema
|
|
89
|
+
AND constraints.constraint_name = usage.constraint_name
|
|
90
|
+
AND constraints.table_schema = usage.table_schema
|
|
91
|
+
AND constraints.table_name = usage.table_name
|
|
92
|
+
LEFT JOIN pg_catalog.pg_namespace local_namespace
|
|
93
|
+
ON local_namespace.nspname = constraints.table_schema
|
|
94
|
+
LEFT JOIN pg_catalog.pg_class local_table
|
|
95
|
+
ON local_table.relname = constraints.table_name
|
|
96
|
+
AND local_table.relnamespace = local_namespace.oid
|
|
97
|
+
LEFT JOIN pg_catalog.pg_constraint foreign_constraint
|
|
98
|
+
ON foreign_constraint.conrelid = local_table.oid
|
|
99
|
+
AND foreign_constraint.conname = constraints.constraint_name
|
|
100
|
+
AND foreign_constraint.contype = 'f'
|
|
101
|
+
LEFT JOIN pg_catalog.pg_class foreign_table
|
|
102
|
+
ON foreign_table.oid = foreign_constraint.confrelid
|
|
103
|
+
LEFT JOIN pg_catalog.pg_namespace foreign_namespace
|
|
104
|
+
ON foreign_namespace.oid = foreign_table.relnamespace
|
|
105
|
+
LEFT JOIN pg_catalog.pg_attribute foreign_column
|
|
106
|
+
ON foreign_column.attrelid = foreign_constraint.confrelid
|
|
107
|
+
AND foreign_column.attnum = foreign_constraint.confkey[usage.ordinal_position]
|
|
108
|
+
WHERE constraints.table_name = ${table}
|
|
109
|
+
AND constraints.table_schema = ${schema}
|
|
110
|
+
ORDER BY usage.constraint_name, usage.ordinal_position
|
|
93
111
|
`
|
|
94
112
|
const constraintsList = await db.query(query)
|
|
95
113
|
return constraintsList
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-mapper",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.64.0",
|
|
4
4
|
"description": "A data mapper utility for SQL databases",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@opentelemetry/api": "^1.9.0",
|
|
20
20
|
"cleaner-spec-reporter": "^0.5.0",
|
|
21
21
|
"eslint": "9",
|
|
22
|
-
"fastify": "^5.
|
|
22
|
+
"fastify": "^5.0.0",
|
|
23
23
|
"neostandard": "^0.12.0",
|
|
24
24
|
"tstyche": "^6.2.0",
|
|
25
25
|
"typescript": "^5.9.3"
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"@matteo.collina/sqlite-pool": "^0.7.0",
|
|
34
34
|
"async-cache-dedupe": "^2.1.0",
|
|
35
35
|
"camelcase": "^6.3.0",
|
|
36
|
-
"fastify-plugin": "^
|
|
36
|
+
"fastify-plugin": "^6.0.0",
|
|
37
37
|
"inflected": "^2.1.0",
|
|
38
|
-
"@platformatic/foundation": "3.
|
|
39
|
-
"@platformatic/telemetry": "3.
|
|
38
|
+
"@platformatic/foundation": "3.64.0",
|
|
39
|
+
"@platformatic/telemetry": "3.64.0"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=22.19.0"
|