Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/datadog-instrumentations/src/helpers/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module.exports = {
'light-my-request': () => require('../light-my-request'),
'limitd-client': () => require('../limitd-client'),
lodash: () => require('../lodash'),
mariadb: () => require('../mariadb'),
mariadb: { esmFirst: true, fn: () => require('../mariadb') },
memcached: () => require('../memcached'),
'microgateway-core': () => require('../microgateway-core'),
mocha: () => require('../mocha'),
Expand Down
106 changes: 83 additions & 23 deletions packages/datadog-instrumentations/src/mariadb.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@
const errorCh = channel('apm:mariadb:query:error')
const skipCh = channel('apm:mariadb:pool:skip')

function wrapCommandStart (start, ctx) {
function wrapCommandStart(start, ctx) {

Check failure on line 14 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return shimmer.wrapFunction(start, start => function () {
if (!startCh.hasSubscribers) return start.apply(this, arguments)

const { reject, resolve } = this
shimmer.wrap(this, 'resolve', function wrapResolve () {
shimmer.wrap(this, 'resolve', function wrapResolve() {

Check failure on line 19 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function () {
return finishCh.runStores(ctx, resolve, this, ...arguments)
}
})

shimmer.wrap(this, 'reject', function wrapReject () {
shimmer.wrap(this, 'reject', function wrapReject() {

Check failure on line 25 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function (error) {
ctx.error = error

Expand All @@ -36,24 +36,26 @@
})
}

function wrapCommand (Command) {
return class extends Command {
constructor (...args) {
super(...args)
function wrapCommand(Command) {

Check failure on line 39 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
if (!Command.prototype.start) return Command

if (!this.start) return
shimmer.wrap(Command.prototype, 'start', function (start) {
return function wrappedStart () {
if (!startCh.hasSubscribers) return start.apply(this, arguments)

const ctx = { sql: this.sql, conf: this.opts }

commandAddCh.publish(ctx)

this.start = wrapCommandStart(this.start, ctx)
return wrapCommandStart(start, ctx).apply(this, arguments)
}
}
})

return Command
}

function createWrapQuery (options) {
return function wrapQuery (query) {
function createWrapQuery(options) {

Check failure on line 57 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function wrapQuery(query) {

Check failure on line 58 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function (sql) {
if (!startCh.hasSubscribers) return query.apply(this, arguments)

Expand All @@ -74,8 +76,8 @@
}
}

function createWrapQueryCallback (options) {
return function wrapQuery (query) {
function createWrapQueryCallback(options) {

Check failure on line 79 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function wrapQuery(query) {

Check failure on line 80 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function (sql) {
if (!startCh.hasSubscribers) return query.apply(this, arguments)

Expand Down Expand Up @@ -104,7 +106,7 @@
}
}

function wrapConnection (promiseMethod, Connection) {
function wrapConnection(promiseMethod, Connection) {

Check failure on line 109 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function (options) {
Connection.apply(this, arguments)

Expand All @@ -113,7 +115,7 @@
}
}

function wrapPoolBase (PoolBase) {
function wrapPoolBase(PoolBase) {

Check failure on line 118 in packages/datadog-instrumentations/src/mariadb.js

View workflow job for this annotation

GitHub Actions / lint

Missing space before function parentheses
return function (options, processTask, createConnectionPool, pingPromise) {
arguments[1] = wrapPoolMethod(processTask)
arguments[2] = wrapPoolMethod(createConnectionPool)
Expand All @@ -127,14 +129,14 @@
// It's not possible to prevent connection pools from leaking across queries,
// so instead we just skip instrumentation completely to avoid memory leaks
// and/or orphan spans.
function wrapPoolMethod (createConnection) {
function wrapPoolMethod(createConnection) {
return function () {
return skipCh.runStores({}, createConnection, this, ...arguments)
}
}

function wrapPoolGetConnectionMethod (getConnection) {
return function wrappedGetConnection () {
function wrapPoolGetConnectionMethod(getConnection) {
return function wrappedGetConnection() {
const cb = arguments[arguments.length - 1]
if (typeof cb !== 'function') return getConnection.apply(this, arguments)

Expand All @@ -152,27 +154,34 @@

const name = 'mariadb'

addHook({ name, file: 'lib/cmd/query.js', versions: ['>=3'] }, (Query) => {
addHook({ name, file: 'lib/cmd/query.js', versions: ['>=3'], patchDefault: true }, (Query) => {
return wrapCommand(Query)
})

addHook({ name, file: 'lib/cmd/execute.js', versions: ['>=3'] }, (Execute) => {
addHook({ name, file: 'lib/cmd/execute.js', versions: ['>=3'], patchDefault: true }, (Execute) => {
return wrapCommand(Execute)
})

// in 3.4.1 getConnection method start to use callbacks instead of promises
addHook({ name, file: 'lib/pool.js', versions: ['>=3.4.1'] }, (Pool) => {
addHook({ name, file: 'lib/pool.js', versions: ['>=3.4.1'], patchDefault: true }, (Pool) => {
shimmer.wrap(Pool.prototype, 'getConnection', wrapPoolGetConnectionMethod)

return Pool
})

addHook({ name, file: 'lib/pool.js', versions: ['>=3'] }, (Pool) => {
// _createConnection was renamed to _createPoolConnection in 3.5.1 alongside the ESM migration
addHook({ name, file: 'lib/pool.js', versions: ['>=3 <3.5.1'] }, (Pool) => {
shimmer.wrap(Pool.prototype, '_createConnection', wrapPoolMethod)

return Pool
})

addHook({ name, file: 'lib/pool.js', versions: ['>=3.5.1'], patchDefault: true }, (Pool) => {
shimmer.wrap(Pool.prototype, '_createPoolConnection', wrapPoolMethod)

return Pool
})

addHook({ name, file: 'lib/connection.js', versions: ['>=2.5.2 <3'] }, (Connection) => {
return shimmer.wrapFunction(Connection, wrapConnection.bind(null, '_queryPromise'))
})
Expand All @@ -184,3 +193,54 @@
addHook({ name, file: 'lib/pool-base.js', versions: ['>=2.0.4 <3'] }, (PoolBase) => {
return shimmer.wrapFunction(PoolBase, wrapPoolBase)
})

// mariadb >= 3.5.1 migrated to pure ESM. require(esm) in Node.js does not trigger
// module.register() hooks, so iitm cannot intercept internal ESM files.
// Instead, hook the public entry points (callback.js / promise.js) via ritm and wrap
// query/execute methods on connection and pool instances directly.
addHook({ name, file: 'callback.js', versions: ['>=3.5.1'] }, (mariadbCallback) => {
const wrapped = {}
for (const key of Object.keys(mariadbCallback)) {
wrapped[key] = mariadbCallback[key]
}

wrapped.createConnection = function (opts) {
const conn = mariadbCallback.createConnection(opts)
shimmer.wrap(conn, 'query', createWrapQueryCallback(opts))
shimmer.wrap(conn, 'execute', createWrapQueryCallback(opts))
return conn
}

wrapped.createPool = function (opts) {
const pool = mariadbCallback.createPool(opts)
shimmer.wrap(pool, 'query', createWrapQueryCallback(opts))
shimmer.wrap(pool, 'execute', createWrapQueryCallback(opts))
return pool
}

return wrapped
})

addHook({ name, versions: ['>=3.5.1'] }, (mariadbPromise) => {
const wrapped = {}
for (const key of Object.keys(mariadbPromise)) {
wrapped[key] = mariadbPromise[key]
}

wrapped.createConnection = function (opts) {
return mariadbPromise.createConnection(opts).then(function (conn) {
shimmer.wrap(conn, 'query', createWrapQuery(opts))
shimmer.wrap(conn, 'execute', createWrapQuery(opts))
return conn
})
}

wrapped.createPool = function (opts) {
const pool = mariadbPromise.createPool(opts)
shimmer.wrap(pool, 'query', createWrapQuery(opts))
shimmer.wrap(pool, 'execute', createWrapQuery(opts))
return pool
}

return wrapped
})
4 changes: 3 additions & 1 deletion packages/datadog-plugin-mariadb/test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const { ANY_STRING, assertObjectContains } = require('../../../integration-tests
const { expectedSchema, rawExpectedSchema } = require('./naming')

// https://github.com/mariadb-corporation/mariadb-connector-nodejs/commit/0a90b71ab20ab4e8b6a86a77ba291bba8ba6a34e
const range = semver.gte(process.version, '15.0.0') ? '>=2.5.1' : '>=2'
// mariadb >=3.5.1 is pure ESM; require() does not trigger the ESM loader hooks needed
// to instrument internal files like lib/pool.js. ESM usage is covered by integration tests.
const range = semver.gte(process.version, '15.0.0') ? '>=2.5.1 <3.5.1' : '>=2 <3.5.1'

describe('Plugin', () => {
describe('mariadb', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ describe('esm', () => {
let proc
let variants

// test against later versions because server.mjs uses newer package syntax
withVersions('mariadb', 'mariadb', '>=3.0.0', version => {
// test against later CJS versions; >=3.5.1 is pure ESM with no default export,
// so iitm cannot intercept it — covered by ritm hooks in index.spec.js instead.
withVersions('mariadb', 'mariadb', '>=3.0.0 <3.5.1', version => {
useSandbox([`'mariadb@${version}'`], false, [
'./packages/datadog-plugin-mariadb/test/integration-test/*'])

Expand Down
2 changes: 1 addition & 1 deletion packages/dd-trace/test/plugins/versions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
"limitd-client": "2.14.1",
"lodash": "4.17.21",
"loopback": "3.28.0",
"mariadb": "3.4.5",
"mariadb": "3.5.2",
"memcached": "2.2.2",
"microgateway-core": "3.3.7",
"middie": "7.1.0",
Expand Down
1 change: 1 addition & 0 deletions xunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<testsuite name="Mocha Tests" tests="41" failures="0" errors="2" skipped="-2" timestamp="Tue, 31 Mar 2026 20:00:12 GMT" time="0.517">
Loading