@platformatic/sql-mapper 3.66.0 → 3.68.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.js +21 -1
- package/lib/connection-info.js +1 -0
- package/lib/entity.js +31 -5
- package/lib/queries/mariadb.js +1 -2
- package/lib/queries/mysql-shared.js +14 -0
- package/lib/queries/mysql.js +10 -0
- package/lib/telemetry.js +1 -1
- package/package.json +4 -4
package/index.js
CHANGED
|
@@ -192,7 +192,7 @@ export async function createConnectionPool ({
|
|
|
192
192
|
sql = createConnectionPoolMysql.sql
|
|
193
193
|
const version = (await db.query(sql`SELECT VERSION()`))[0]['VERSION()']
|
|
194
194
|
db.version = version
|
|
195
|
-
db.isMariaDB = version.indexOf('maria') !== -1
|
|
195
|
+
db.isMariaDB = version.toLowerCase().indexOf('maria') !== -1
|
|
196
196
|
if (!db.isMariaDB) {
|
|
197
197
|
db.isMySql = true
|
|
198
198
|
}
|
|
@@ -312,6 +312,26 @@ export async function connect ({
|
|
|
312
312
|
wrap.constraints = await queries.listConstraints(db, sql, table, schema)
|
|
313
313
|
wrap.columns = columns
|
|
314
314
|
|
|
315
|
+
// MariaDB reports JSON columns as `longtext`; recover the JSON-ness
|
|
316
|
+
// from the auto-generated json_valid(...) CHECK constraint.
|
|
317
|
+
// Fail open: information_schema.CHECK_CONSTRAINTS (and its TABLE_NAME
|
|
318
|
+
// extension) is only present on MariaDB >= 10.2.1. On older MariaDB,
|
|
319
|
+
// or if permissions are restricted, just skip JSON-column detection
|
|
320
|
+
// instead of breaking introspection entirely.
|
|
321
|
+
if (db.isMariaDB && queries.listJsonColumns) {
|
|
322
|
+
try {
|
|
323
|
+
const jsonColumns = await queries.listJsonColumns(db, sql, table, schema)
|
|
324
|
+
for (const jsonColumn of jsonColumns) {
|
|
325
|
+
const column = columns.find(c => c.column_name === jsonColumn.column_name)
|
|
326
|
+
if (column) {
|
|
327
|
+
column.isJson = true
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
// information_schema.CHECK_CONSTRAINTS not available/queryable; ignore.
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
315
335
|
// To get enum values in pg
|
|
316
336
|
/* istanbul ignore next */
|
|
317
337
|
if (db.isPg) {
|
package/lib/connection-info.js
CHANGED
package/lib/entity.js
CHANGED
|
@@ -66,7 +66,7 @@ function createMapper (
|
|
|
66
66
|
return acc
|
|
67
67
|
}, {})
|
|
68
68
|
|
|
69
|
-
const primaryKeysTypes = Array.from(primaryKeys).map(key => {
|
|
69
|
+
const primaryKeysTypes = Array.from(primaryKeys).map((key) => {
|
|
70
70
|
return {
|
|
71
71
|
key,
|
|
72
72
|
sqlType: fields[key].sqlType
|
|
@@ -99,25 +99,50 @@ function createMapper (
|
|
|
99
99
|
if (!output) {
|
|
100
100
|
return output
|
|
101
101
|
}
|
|
102
|
+
|
|
102
103
|
const newOutput = {}
|
|
104
|
+
|
|
103
105
|
for (const key of Object.keys(output)) {
|
|
104
106
|
let value = output[key]
|
|
105
107
|
const newKey = fieldMapToRetrieve[key]
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
|
|
109
|
+
const isNativeJson =
|
|
110
|
+
fields[key]?.sqlType === 'json' || fields[key]?.sqlType === 'jsonb'
|
|
111
|
+
|
|
112
|
+
// MariaDB has no native JSON type: JSON columns are stored (and
|
|
113
|
+
// introspected) as `longtext`. We mark them at introspection time
|
|
114
|
+
// via the `isJson` flag (see index.js / lib/queries/mysql-shared.js).
|
|
115
|
+
const isJsonText = fields[key]?.isJson === true
|
|
116
|
+
|
|
117
|
+
if (isNativeJson || isJsonText) {
|
|
118
|
+
// The driver may already have deserialized the value (some MariaDB
|
|
119
|
+
// driver versions do this for longtext columns), or it may still be
|
|
120
|
+
// the raw JSON string - normalize to a parsed object either way.
|
|
121
|
+
if (typeof value === 'string') {
|
|
122
|
+
value = JSON.parse(value)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
newOutput[newKey] = value
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
|
|
108
129
|
if (
|
|
109
130
|
(primaryKeys.has(key) || fields[key]?.stringifyOutput) &&
|
|
110
131
|
value !== null &&
|
|
111
132
|
value !== undefined &&
|
|
112
|
-
!(value instanceof Date)
|
|
133
|
+
!(value instanceof Date) &&
|
|
134
|
+
typeof value !== 'object'
|
|
113
135
|
) {
|
|
114
136
|
value = value.toString()
|
|
115
137
|
}
|
|
138
|
+
|
|
116
139
|
if (newKey && isVectorType(fields[key].sqlType)) {
|
|
117
140
|
value = parseVector(value)
|
|
118
141
|
}
|
|
142
|
+
|
|
119
143
|
newOutput[newKey] = value
|
|
120
144
|
}
|
|
145
|
+
|
|
121
146
|
return newOutput
|
|
122
147
|
}
|
|
123
148
|
|
|
@@ -522,7 +547,8 @@ export function buildEntity (
|
|
|
522
547
|
sqlType: column.udt_name,
|
|
523
548
|
isNullable: column.is_nullable === 'YES',
|
|
524
549
|
isArray: column.isArray,
|
|
525
|
-
vectorDimensions: column.vectorDimensions
|
|
550
|
+
vectorDimensions: column.vectorDimensions,
|
|
551
|
+
isJson: column.isJson === true
|
|
526
552
|
}
|
|
527
553
|
|
|
528
554
|
// To get enum values in mysql and mariadb
|
package/lib/queries/mariadb.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { deleteAll, insertMany, insertOne } from './shared.js'
|
|
1
|
+
export * from './mysql.js'
|
|
@@ -34,6 +34,20 @@ export async function listColumns (db, sql, table, schema) {
|
|
|
34
34
|
return db.query(query)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// MariaDB has no native JSON type: a `JSON` column is really a LONGTEXT
|
|
38
|
+
// column with an automatically-generated CHECK (json_valid(`col`)) constraint.
|
|
39
|
+
// We use that constraint to recognize which longtext columns are actually JSON.
|
|
40
|
+
export async function listJsonColumns (db, sql, table, schema) {
|
|
41
|
+
const query = sql`
|
|
42
|
+
SELECT CONSTRAINT_NAME as column_name
|
|
43
|
+
FROM information_schema.CHECK_CONSTRAINTS
|
|
44
|
+
WHERE TABLE_NAME = ${table}
|
|
45
|
+
AND CONSTRAINT_SCHEMA = ${schema}
|
|
46
|
+
AND CHECK_CLAUSE LIKE 'json_valid(%'
|
|
47
|
+
`
|
|
48
|
+
return db.query(query)
|
|
49
|
+
}
|
|
50
|
+
|
|
37
51
|
export async function listConstraints (db, sql, table, schema) {
|
|
38
52
|
const query = sql`
|
|
39
53
|
SELECT TABLE_NAME as table_name, TABLE_SCHEMA as table_schema, COLUMN_NAME as column_name, CONSTRAINT_TYPE as constraint_type, referenced_table_name AS foreign_table_name, referenced_table_schema AS foreign_table_schema, referenced_column_name AS foreign_column_name
|
package/lib/queries/mysql.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
1
2
|
import { MissingValueForPrimaryKeyError } from '../errors.js'
|
|
2
3
|
import { tableName } from '../utils.js'
|
|
3
4
|
import { insertPrep } from './shared.js'
|
|
@@ -5,6 +6,15 @@ import { insertPrep } from './shared.js'
|
|
|
5
6
|
export * from './mysql-shared.js'
|
|
6
7
|
|
|
7
8
|
export function insertOne (db, sql, table, schema, input, primaryKeys, fieldsToRetrieve) {
|
|
9
|
+
// last_insert_id() only reflects AUTO_INCREMENT columns. A single uuid
|
|
10
|
+
// primary key relying on a server-side DEFAULT (e.g. `DEFAULT UUID()` on
|
|
11
|
+
// MariaDB) can't be recovered that way, so generate it here instead and
|
|
12
|
+
// insert it explicitly - this also lets us fetch the row back by its
|
|
13
|
+
// known id, same as the "primary key provided" path below.
|
|
14
|
+
if (primaryKeys.length === 1 && primaryKeys[0].sqlType === 'uuid' && input[primaryKeys[0].key] === undefined) {
|
|
15
|
+
input = { ...input, [primaryKeys[0].key]: randomUUID() }
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
const keysToSql = Object.keys(input).map(key => sql.ident(key))
|
|
9
19
|
const keys = sql.join(keysToSql, sql`, `)
|
|
10
20
|
|
package/lib/telemetry.js
CHANGED
|
@@ -9,7 +9,7 @@ export function wrapQuery (app, db, request) {
|
|
|
9
9
|
let namePrefix
|
|
10
10
|
if (connectionInfo.isPg) {
|
|
11
11
|
namePrefix = 'pg.query:'
|
|
12
|
-
} else if (connectionInfo.isMySql) {
|
|
12
|
+
} else if (connectionInfo.isMySql || connectionInfo.isMariaDB) {
|
|
13
13
|
namePrefix = 'mysql.query:'
|
|
14
14
|
} else if (connectionInfo.isSQLite) {
|
|
15
15
|
namePrefix = 'sqlite.query:'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-mapper",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.68.0",
|
|
4
4
|
"description": "A data mapper utility for SQL databases",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"fastify": "^5.0.0",
|
|
23
23
|
"neostandard": "^0.12.0",
|
|
24
24
|
"tstyche": "^6.2.0",
|
|
25
|
-
"typescript": "
|
|
25
|
+
"typescript": "~6.0.3"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@databases/mysql": "^7.0.0",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"camelcase": "^6.3.0",
|
|
36
36
|
"fastify-plugin": "^6.0.0",
|
|
37
37
|
"inflected": "^2.1.0",
|
|
38
|
-
"@platformatic/foundation": "3.
|
|
39
|
-
"@platformatic/telemetry": "3.
|
|
38
|
+
"@platformatic/foundation": "3.68.0",
|
|
39
|
+
"@platformatic/telemetry": "3.68.0"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=22.19.0"
|