From bb7e2f0f5e5d943b3709301c865f02c77aa6b06f Mon Sep 17 00:00:00 2001 From: Crystal Luc-Magloire Date: Mon, 30 Mar 2026 11:09:47 -0400 Subject: [PATCH 1/4] feat(mariadb): migrate instrumentation from shimmer to orchestrion rewriter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the legacy shimmer-based mariadb plugin with an orchestrion AST-rewriter approach, enabling proper async context propagation and eliminating the need for manual module patching. Key changes: - Add orchestrion instrumentation entries for mariadb (v2 and v3+) covering createConnection, createPool, Pool.getConnection, and all query/execute methods across callback and promise APIs - Add thisPropertyName query type to the rewriter for arrow function and regular function instance properties set in constructors - Add noCallbackFallback option to wrapCallback for fire-and-forget queries - Fix ArrowFunctionExpression → FunctionExpression conversion in traceFunction so arguments and this bindings work correctly - New plugin split into connection.js (context/conf tracking) and query.js (span creation), replacing the monolithic MySQLPlugin extension - Properly clear async context during createPool/createConnection so pool-internal TCP connections don't leak into user traces - Restore parent span context in callback/promise completions via bindAsyncStart - Tag peer.service in asyncEnd (orchestrion never fires the finish channel) - Support PrepareResultPacket.execute for prepared statement tracing Co-Authored-By: Claude Sonnet 4.6 --- eslint.config.mjs | 2 + .../rewriter/instrumentations/index.js | 1 + .../rewriter/instrumentations/mariadb.js | 350 ++++++++++++++++++ .../datadog-instrumentations/src/mariadb.js | 185 +-------- .../test/helpers/rewriter/index.spec.js | 25 ++ .../test-trace-this-property/index.js | 16 + .../test-trace-this-property/package.json | 1 + .../datadog-plugin-mariadb/src/connection.js | 209 +++++++++++ packages/datadog-plugin-mariadb/src/index.js | 60 ++- packages/datadog-plugin-mariadb/src/query.js | 210 +++++++++++ .../datadog-plugin-mariadb/test/index.spec.js | 23 +- 11 files changed, 879 insertions(+), 203 deletions(-) create mode 100644 packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js create mode 100644 packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js create mode 100644 packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json create mode 100644 packages/datadog-plugin-mariadb/src/connection.js create mode 100644 packages/datadog-plugin-mariadb/src/query.js diff --git a/eslint.config.mjs b/eslint.config.mjs index 3a39c81ebaa..63b93836fb1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -693,6 +693,8 @@ export default [ 'integration-tests/**/*.mjs', 'packages/*/test/integration-test/**/*.js', 'packages/*/test/integration-test/**/*.mjs', + '.claude/worktrees/*/packages/*/test/integration-test/**/*.js', + '.claude/worktrees/*/packages/*/test/integration-test/**/*.mjs', // TODO: Move the files in esm-test to integration-test 'packages/datadog-plugin-graphql/test/esm-test/**/*.mjs', 'packages/dd-trace/test/appsec/**/resources/**/*.js', diff --git a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js index 28b13f15191..9b917cf03a5 100644 --- a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +++ b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js @@ -5,4 +5,5 @@ module.exports = [ ...require('./bullmq'), ...require('./langchain'), ...require('./langgraph'), + ...require('./mariadb'), ] diff --git a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js new file mode 100644 index 00000000000..c92d368a22a --- /dev/null +++ b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js @@ -0,0 +1,350 @@ +'use strict' + +module.exports = [ + // ------------------------------------------------------------------------- + // Orchestrion entries — these hooks are loaded via getHooks('mariadb') + // ------------------------------------------------------------------------- + // Pool.getConnection(callback) — callback-based; NOT returning a promise. + // Using kind: 'Callback' so the connection is available via ctx.result in + // asyncStart.runStores, enabling correct conf propagation and context restore. + { + module: { + name: 'mariadb', + versionRange: '>=3.4.1', + filePath: 'lib/pool.js', + }, + functionQuery: { + methodName: 'getConnection', + className: 'Pool', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'Pool_getConnection', + }, + // ------------------------------------------------------------------------- + // v2 constructor hooks — stash connection opts on the instance as __ddConf + // ------------------------------------------------------------------------- + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/connection.js', + }, + functionQuery: { + functionName: 'Connection', + kind: 'Sync', + }, + channelName: 'v2Connection', + }, + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/pool-base.js', + }, + functionQuery: { + functionName: 'PoolBase', + kind: 'Sync', + }, + channelName: 'v2PoolBase', + }, + + // ------------------------------------------------------------------------- + // v2 query hooks — use thisPropertyName to target arrow-function instance + // properties set inside function constructors. + // ------------------------------------------------------------------------- + + // v>=2.5.2 <3: _queryPromise (promise API) + { + module: { + name: 'mariadb', + versionRange: '>=2.5.2 <3', + filePath: 'lib/connection.js', + }, + functionQuery: { + thisPropertyName: '_queryPromise', + kind: 'Async', + }, + channelName: 'v2Connection_queryPromise', + }, + // v>=2.0.4 <=2.5.1: query (promise API) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <=2.5.1', + filePath: 'lib/connection.js', + }, + functionQuery: { + thisPropertyName: 'query', + kind: 'Async', + }, + channelName: 'v2Connection_query', + }, + // All v2: _queryCallback (callback API) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/connection.js', + }, + functionQuery: { + thisPropertyName: '_queryCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'v2Connection_queryCallback', + }, + // v2 pool: getConnection (promise API — context clearing + conf propagation) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/pool-base.js', + }, + functionQuery: { + thisPropertyName: 'getConnection', + kind: 'Async', + }, + channelName: 'v2PoolBase_getConnection', + }, + // v2 pool: query (promise API) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/pool-base.js', + }, + functionQuery: { + thisPropertyName: 'query', + kind: 'Async', + }, + channelName: 'v2PoolBase_query', + }, + + // ------------------------------------------------------------------------- + // v2 callback.js — createPool/createConnection hooks + // In v2, callback.js calls pool.initialize() right after construction, + // which creates the first TCP connection. Hooking createPool clears context + // so that initial connection doesn't become a child of the user's span. + // ------------------------------------------------------------------------- + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createPool', + kind: 'Sync', + }, + channelName: 'createPool', + }, + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createConnection', + kind: 'Sync', + }, + channelName: 'createConnection', + }, + + // ------------------------------------------------------------------------- + // Shimmer entries — v>=3 query/execute hooks that cannot use orchestrion + // because the AST rewriter moves the constructor body into a nested + // function, which breaks super() calls (SyntaxError). + // ------------------------------------------------------------------------- + + // callback.js — createConnection (sync wrapper to capture opts) + // Uses expressionName because these are named function expressions + // assigned to module.exports, not function declarations. + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createConnection', + kind: 'Sync', + }, + channelName: 'createConnection', + }, + // callback.js — createPool (sync wrapper to capture opts) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createPool', + kind: 'Sync', + }, + channelName: 'createPool', + }, + // promise.js — createConnection (async — returns promise) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'promise.js', + }, + functionQuery: { + expressionName: 'createConnection', + kind: 'Async', + }, + channelName: 'createConnection', + }, + // promise.js — createPool (sync wrapper to capture opts) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'promise.js', + }, + functionQuery: { + expressionName: 'createPool', + kind: 'Sync', + }, + channelName: 'createPool', + }, + // ConnectionCallback.prototype.query (callback-based — callback is last arg) + // noCallbackFallback: query() may be called without a callback (event-emitter usage) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-callback.js', + }, + functionQuery: { + methodName: 'query', + className: 'ConnectionCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'ConnectionCallback_query', + }, + // ConnectionCallback.prototype.execute (callback-based — callback is last arg) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-callback.js', + }, + functionQuery: { + methodName: 'execute', + className: 'ConnectionCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'ConnectionCallback_execute', + }, + // ConnectionPromise.prototype.query (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-promise.js', + }, + functionQuery: { + methodName: 'query', + className: 'ConnectionPromise', + kind: 'Async', + }, + channelName: 'ConnectionPromise_query', + }, + // ConnectionPromise.prototype.execute (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-promise.js', + }, + functionQuery: { + methodName: 'execute', + className: 'ConnectionPromise', + kind: 'Async', + }, + channelName: 'ConnectionPromise_execute', + }, + // PoolCallback.prototype.query (callback-based — callback is last arg) + // noCallbackFallback: pool.query() is often called without a callback (fire-and-forget) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-callback.js', + }, + functionQuery: { + methodName: 'query', + className: 'PoolCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'PoolCallback_query', + }, + // PoolCallback.prototype.execute (callback-based — callback is last arg) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-callback.js', + }, + functionQuery: { + methodName: 'execute', + className: 'PoolCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'PoolCallback_execute', + }, + // PoolPromise.prototype.query (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-promise.js', + }, + functionQuery: { + methodName: 'query', + className: 'PoolPromise', + kind: 'Async', + }, + channelName: 'PoolPromise_query', + }, + // PoolPromise.prototype.execute (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-promise.js', + }, + functionQuery: { + methodName: 'execute', + className: 'PoolPromise', + kind: 'Async', + }, + channelName: 'PoolPromise_execute', + }, + // PrepareResultPacket.prototype.execute — used via PrepareWrapper (statement.execute) + // ctx.self is PrepareWrapper: .query = SQL, .conn.opts = connection options + // noCallbackFallback: execute() may be called without callback (promise mode) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/cmd/class/prepare-result-packet.js', + }, + functionQuery: { + methodName: 'execute', + className: 'PrepareResultPacket', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'PrepareResultPacket_execute', + }, +] diff --git a/packages/datadog-instrumentations/src/mariadb.js b/packages/datadog-instrumentations/src/mariadb.js index eb7cee830f7..685d92dc45a 100644 --- a/packages/datadog-instrumentations/src/mariadb.js +++ b/packages/datadog-instrumentations/src/mariadb.js @@ -1,186 +1,7 @@ 'use strict' -const shimmer = require('../../datadog-shimmer') -const { channel, addHook } = require('./helpers/instrument') +const { addHook, getHooks } = require('./helpers/instrument') -const commandAddCh = channel('apm:mariadb:command:add') -const connectionStartCh = channel('apm:mariadb:connection:start') -const connectionFinishCh = channel('apm:mariadb:connection:finish') -const startCh = channel('apm:mariadb:query:start') -const finishCh = channel('apm:mariadb:query:finish') -const errorCh = channel('apm:mariadb:query:error') -const skipCh = channel('apm:mariadb:pool:skip') - -function wrapCommandStart (start, ctx) { - return shimmer.wrapFunction(start, start => function () { - if (!startCh.hasSubscribers) return start.apply(this, arguments) - - const { reject, resolve } = this - shimmer.wrap(this, 'resolve', function wrapResolve () { - return function () { - return finishCh.runStores(ctx, resolve, this, ...arguments) - } - }) - - shimmer.wrap(this, 'reject', function wrapReject () { - return function (error) { - ctx.error = error - - errorCh.publish(ctx) - - return finishCh.runStores(ctx, reject, this, ...arguments) - } - }) - - return startCh.runStores(ctx, start, this, ...arguments) - }) -} - -function wrapCommand (Command) { - return class extends Command { - constructor (...args) { - super(...args) - - if (!this.start) return - - const ctx = { sql: this.sql, conf: this.opts } - - commandAddCh.publish(ctx) - - this.start = wrapCommandStart(this.start, ctx) - } - } +for (const hook of getHooks('mariadb')) { + addHook(hook, exports => exports) } - -function createWrapQuery (options) { - return function wrapQuery (query) { - return function (sql) { - if (!startCh.hasSubscribers) return query.apply(this, arguments) - - const ctx = { sql, conf: options } - - return startCh.runStores(ctx, query, this, ...arguments) - .then(result => { - ctx.result = result - finishCh.publish(ctx) - return result - }, error => { - ctx.error = error - errorCh.publish(ctx) - finishCh.publish(ctx) - throw error - }) - } - } -} - -function createWrapQueryCallback (options) { - return function wrapQuery (query) { - return function (sql) { - if (!startCh.hasSubscribers) return query.apply(this, arguments) - - const cb = arguments[arguments.length - 1] - const ctx = { sql, conf: options } - const wrapper = (cb) => function (err) { - if (err) { - ctx.error = err - errorCh.publish(ctx) - } - - return typeof cb === 'function' - ? finishCh.runStores(ctx, cb, this, ...arguments) - : finishCh.publish(ctx) - } - - if (typeof cb === 'function') { - arguments[arguments.length - 1] = shimmer.wrapFunction(cb, wrapper) - } else { - arguments.length += 1 - arguments[arguments.length - 1] = wrapper() - } - - return startCh.runStores(ctx, query, this, ...arguments) - } - } -} - -function wrapConnection (promiseMethod, Connection) { - return function (options) { - Connection.apply(this, arguments) - - shimmer.wrap(this, promiseMethod, createWrapQuery(options)) - shimmer.wrap(this, '_queryCallback', createWrapQueryCallback(options)) - } -} - -function wrapPoolBase (PoolBase) { - return function (options, processTask, createConnectionPool, pingPromise) { - arguments[1] = wrapPoolMethod(processTask) - arguments[2] = wrapPoolMethod(createConnectionPool) - - PoolBase.apply(this, arguments) - - shimmer.wrap(this, 'query', createWrapQuery(options.connOptions)) - } -} - -// 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) { - return function () { - return skipCh.runStores({}, createConnection, this, ...arguments) - } -} - -function wrapPoolGetConnectionMethod (getConnection) { - return function wrappedGetConnection () { - const cb = arguments[arguments.length - 1] - if (typeof cb !== 'function') return getConnection.apply(this, arguments) - - const ctx = {} - - arguments[arguments.length - 1] = function () { - return connectionFinishCh.runStores(ctx, cb, this, ...arguments) - } - - connectionStartCh.publish(ctx) - - return getConnection.apply(this, arguments) - } -} - -const name = 'mariadb' - -addHook({ name, file: 'lib/cmd/query.js', versions: ['>=3'] }, (Query) => { - return wrapCommand(Query) -}) - -addHook({ name, file: 'lib/cmd/execute.js', versions: ['>=3'] }, (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) => { - shimmer.wrap(Pool.prototype, 'getConnection', wrapPoolGetConnectionMethod) - - return Pool -}) - -addHook({ name, file: 'lib/pool.js', versions: ['>=3'] }, (Pool) => { - shimmer.wrap(Pool.prototype, '_createConnection', wrapPoolMethod) - - return Pool -}) - -addHook({ name, file: 'lib/connection.js', versions: ['>=2.5.2 <3'] }, (Connection) => { - return shimmer.wrapFunction(Connection, wrapConnection.bind(null, '_queryPromise')) -}) - -addHook({ name, file: 'lib/connection.js', versions: ['>=2.0.4 <=2.5.1'] }, (Connection) => { - return shimmer.wrapFunction(Connection, wrapConnection.bind(null, 'query')) -}) - -addHook({ name, file: 'lib/pool-base.js', versions: ['>=2.0.4 <3'] }, (PoolBase) => { - return shimmer.wrapFunction(PoolBase, wrapPoolBase) -}) diff --git a/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js b/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js index f95e8b3c937..8928e0f4ed4 100644 --- a/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js +++ b/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js @@ -261,6 +261,18 @@ describe('check-require-cache', () => { }, channelName: 'trace_class_private_method', }, + { + module: { + name: 'test-trace-this-property', + versionRange: '>=0.1', + filePath: 'index.js', + }, + functionQuery: { + thisPropertyName: 'test', + kind: 'Sync', + }, + channelName: 'test_invoke', + }, ], }) }) @@ -515,4 +527,17 @@ describe('check-require-cache', () => { assert.ok(subs.start.called) }) + + it('should auto instrument arrow function instance properties (thisPropertyName)', done => { + const { test } = compile('test-trace-this-property') + + subs = { + start: () => setImmediate(done), + } + + ch = tracingChannel('orchestrion:test-trace-this-property:test_invoke') + ch.subscribe(subs) + + test() + }) }) diff --git a/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js b/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js new file mode 100644 index 00000000000..e48ddd58433 --- /dev/null +++ b/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js @@ -0,0 +1,16 @@ +'use strict' + +// Simulates old-style function constructor where a query method is +// assigned as an arrow-function instance property (as in mariadb v2). +function Foo (opts) { + this.opts = opts + + this.test = () => {} +} + +function test () { + const foo = new Foo({ host: 'localhost' }) + foo.test('SELECT 1') +} + +module.exports = { test } diff --git a/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json b/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json new file mode 100644 index 00000000000..612148f7f42 --- /dev/null +++ b/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json @@ -0,0 +1 @@ +{ "name": "test-trace-this-property", "version": "1.0.0" } diff --git a/packages/datadog-plugin-mariadb/src/connection.js b/packages/datadog-plugin-mariadb/src/connection.js new file mode 100644 index 00000000000..1b1ce76ae3a --- /dev/null +++ b/packages/datadog-plugin-mariadb/src/connection.js @@ -0,0 +1,209 @@ +'use strict' + +const { storage } = require('../../datadog-core') +const Plugin = require('../../dd-trace/src/plugins/plugin') + +const DD_CONF = '__ddConf' + +/** + * Extracts connection-relevant config from the raw user options. + * + * @param {object} opts - Raw options passed to createConnection or createPool + * @returns {{ host?: string, user?: string, database?: string, port?: number }} + */ +function extractConf (opts) { + if (!opts || typeof opts !== 'object') return {} + return { + host: opts.host, + user: opts.user, + database: opts.database, + port: opts.port, + } +} + +/** + * Base class for connection/pool factory tracking. + * + * Listens on the createConnection / createPool orchestrion channels and + * stashes the raw user-supplied connection options onto the returned + * instance so that query plugins can read them as `ctx.self.__ddConf`. + */ +class MariadbConnectionTrackingPlugin extends Plugin { + static id = 'mariadb' + + constructor () { + super(...arguments) + + const prefix = this.constructor.prefix + + // Clear context during createConnection/createPool so that pool-internal + // TCP connections (e.g. from minimumIdle) don't become children of the + // user's active span. + this.addBind(`${prefix}:start`, () => null) + + this.addSub(`${prefix}:end`, ctx => { + this.storeConf(ctx) + }) + + this.addSub(`${prefix}:asyncEnd`, ctx => { + this.storeConf(ctx) + }) + } + + /** + * Store connection config on the returned instance. + * + * @param {object} ctx - Orchestrion channel context + */ + storeConf (ctx) { + const opts = ctx.arguments?.[0] + const target = ctx.result || ctx.self + + if (target && opts) { + target[DD_CONF] = extractConf(opts) + } + } + + configure (config) { + return super.configure(config) + } +} + +/** + * Handles both callback.js and promise.js createConnection calls. + * Both share the same orchestrion channelName 'createConnection'. + * callback.js is Sync (fires end), promise.js is Async (fires asyncEnd). + */ +class CreateConnectionPlugin extends MariadbConnectionTrackingPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:createConnection' +} + +/** + * Handles both callback.js and promise.js createPool calls. + * Both share the same orchestrion channelName 'createPool'. + * Both are Sync (fire end). + */ +class CreatePoolPlugin extends MariadbConnectionTrackingPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:createPool' +} + +/** + * Handles Pool.getConnection (v>=3.4.1). + * Propagates config from the pool to the returned connection, and + * captures parent store to restore context after pool-internal operations. + */ +class PoolGetConnectionPlugin extends Plugin { + static id = 'mariadb' + + constructor () { + super(...arguments) + + const prefix = 'tracing:orchestrion:mariadb:Pool_getConnection' + + // Save the user's context before start clears it, and return null to clear + // context so pool-internal TCP connections don't become children of the user span. + this.addBind(`${prefix}:start`, ctx => { + ctx.parentStore = storage('legacy').getStore() + return null + }) + + // asyncStart uses runStores (unlike asyncEnd which uses publish), so addBind works here. + // ctx.result is the connection passed to the callback by the pool. + this.addBind(`${prefix}:asyncStart`, ctx => { + const conn = ctx.result + + // Propagate pool config to the returned connection. + // ctx.self is the internal Pool instance which stores PoolOptions on `opts`. + // PoolOptions.connOptions is a ConnOptions with host, user, database, port. + if (conn) { + const poolSelf = ctx.self + if (poolSelf?.[DD_CONF]) { + conn[DD_CONF] = poolSelf[DD_CONF] + } else if (poolSelf?.opts?.connOptions) { + conn[DD_CONF] = extractConf(poolSelf.opts.connOptions) + } + } + + return ctx.parentStore + }) + } + + configure (config) { + return super.configure(config) + } +} + +/** + * Handles v<3 Connection constructor wrapping. + * Stashes config on the new connection instance via ctx.self. + */ +class V2ConnectionPlugin extends MariadbConnectionTrackingPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:v2Connection' +} + +/** + * Handles v<3 PoolBase constructor wrapping. + * Stashes pool connOptions on the new pool instance via ctx.self. + * Pool options nest the connection config under `connOptions`. + */ +class V2PoolBasePlugin extends MariadbConnectionTrackingPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:v2PoolBase' + + storeConf (ctx) { + const opts = ctx.arguments?.[0] + const target = ctx.self + + if (target && opts) { + target[DD_CONF] = extractConf(opts.connOptions || opts) + } + } +} + +/** + * Handles v<3 PoolBase.getConnection calls. + * Clears context during execution so pool-internal TCP connections don't + * become children of the user's span, and propagates pool conf to the + * returned connection. + */ +class V2PoolBaseGetConnectionPlugin extends Plugin { + static id = 'mariadb' + + constructor () { + super(...arguments) + + const prefix = 'tracing:orchestrion:mariadb:v2PoolBase_getConnection' + + this.addBind(`${prefix}:start`, ctx => { + ctx.parentStore = storage('legacy').getStore() + return null + }) + + this.addBind(`${prefix}:asyncStart`, ctx => { + const conn = ctx.result + if (conn) { + const poolSelf = ctx.self + if (poolSelf?.[DD_CONF]) { + conn[DD_CONF] = poolSelf[DD_CONF] + } + } + return ctx.parentStore + }) + } + + configure (config) { + return super.configure(config) + } +} + +module.exports = [ + CreateConnectionPlugin, + CreatePoolPlugin, + PoolGetConnectionPlugin, + V2ConnectionPlugin, + V2PoolBasePlugin, + V2PoolBaseGetConnectionPlugin, +] diff --git a/packages/datadog-plugin-mariadb/src/index.js b/packages/datadog-plugin-mariadb/src/index.js index 60823b0124b..5e20d8f9d03 100644 --- a/packages/datadog-plugin-mariadb/src/index.js +++ b/packages/datadog-plugin-mariadb/src/index.js @@ -1,20 +1,56 @@ 'use strict' -const { storage } = require('../../datadog-core') -const MySQLPlugin = require('../../datadog-plugin-mysql/src') +const CompositePlugin = require('../../dd-trace/src/plugins/composite') +const connectionPlugins = require('./connection') +const queryPlugins = require('./query') -class MariadbPlugin extends MySQLPlugin { - static id = 'mariadb' - static system = 'mariadb' - - constructor (...args) { - super(...args) +const [ + CreateConnectionPlugin, + CreatePoolPlugin, + PoolGetConnectionPlugin, + V2ConnectionPlugin, + V2PoolBasePlugin, + V2PoolBaseGetConnectionPlugin, +] = connectionPlugins - this.addBind(`apm:${this.component}:pool:skip`, () => ({ noop: true })) +const [ + ConnectionCallbackQueryPlugin, + ConnectionCallbackExecutePlugin, + ConnectionPromiseQueryPlugin, + ConnectionPromiseExecutePlugin, + PoolCallbackQueryPlugin, + PoolCallbackExecutePlugin, + PoolPromiseQueryPlugin, + PoolPromiseExecutePlugin, + V2ConnectionQueryPromisePlugin, + V2ConnectionQueryPlugin, + V2ConnectionQueryCallbackPlugin, + V2PoolBaseQueryPlugin, + PreparedStatementCallbackExecutePlugin, +] = queryPlugins - this.addSub(`apm:${this.component}:command:add`, ctx => { - ctx.parentStore = storage('legacy').getStore() - }) +class MariadbPlugin extends CompositePlugin { + static id = 'mariadb' + static plugins = { + createConnection: CreateConnectionPlugin, + createPool: CreatePoolPlugin, + poolGetConnection: PoolGetConnectionPlugin, + v2Connection: V2ConnectionPlugin, + v2PoolBase: V2PoolBasePlugin, + v2PoolGetConnection: V2PoolBaseGetConnectionPlugin, + cbConnQuery: ConnectionCallbackQueryPlugin, + cbConnExecute: ConnectionCallbackExecutePlugin, + promiseConnQuery: ConnectionPromiseQueryPlugin, + promiseConnExecute: ConnectionPromiseExecutePlugin, + cbPoolQuery: PoolCallbackQueryPlugin, + cbPoolExecute: PoolCallbackExecutePlugin, + promisePoolQuery: PoolPromiseQueryPlugin, + promisePoolExecute: PoolPromiseExecutePlugin, + v2ConnQueryPromise: V2ConnectionQueryPromisePlugin, + v2ConnQuery: V2ConnectionQueryPlugin, + v2ConnQueryCallback: V2ConnectionQueryCallbackPlugin, + v2PoolQuery: V2PoolBaseQueryPlugin, + preparedStmtExecute: PreparedStatementCallbackExecutePlugin, } } diff --git a/packages/datadog-plugin-mariadb/src/query.js b/packages/datadog-plugin-mariadb/src/query.js new file mode 100644 index 00000000000..a272eb0b767 --- /dev/null +++ b/packages/datadog-plugin-mariadb/src/query.js @@ -0,0 +1,210 @@ +'use strict' + +const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants') +const DatabasePlugin = require('../../dd-trace/src/plugins/database') + +class MariadbQueryPlugin extends DatabasePlugin { + static id = 'mariadb' + static system = 'mariadb' + static operation = 'query' + + /** + * Extract connection config from the connection/pool instance. + * The createConnection/createPool channel handlers store the raw opts on + * the instance as `__ddConf`. + * + * @param {object} self - The connection or pool instance (ctx.self) + * @returns {{ host?: string, user?: string, database?: string, port?: number }} + */ + getConf (self) { + return self?.__ddConf || {} + } + + bindStart (ctx) { + const conf = this.getConf(ctx.self) + const sql = ctx.arguments?.[0] + const service = this.serviceName({ pluginConfig: this.config, dbConfig: conf, system: this.system }) + + const span = this.startSpan(this.operationName(), { + service, + resource: sql, + type: 'sql', + kind: 'client', + meta: { + 'db.type': this.system, + 'db.user': conf.user, + 'db.name': conf.database, + 'out.host': conf.host, + [CLIENT_PORT_KEY]: conf.port, + }, + }, ctx) + + ctx.sql = this.injectDbmQuery(span, sql, service) + + return ctx.currentStore + } + + /** + * Restore the parent async context when the callback/promise result arrives, + * so that user code inside the callback sees the same active span as before + * the query was called (rather than the DB span context). + * + * @param {object} ctx - Orchestrion channel context + * @returns {object} parentStore - the store active when bindStart ran + */ + bindAsyncStart (ctx) { + return ctx.parentStore + } + + /** + * Finish the span when the async operation completes. + * Tag peer.service before finishing since the orchestrion wrappers never + * fire the 'finish' channel that OutboundPlugin.finish() listens to. + * + * @param {object} ctx - Orchestrion channel context + */ + asyncEnd (ctx) { + const span = ctx.currentStore?.span + if (span) { + this.tagPeerService(span) + span.finish() + } + } +} + +class ConnectionCallbackQueryPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:ConnectionCallback_query' +} + +class ConnectionCallbackExecutePlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:ConnectionCallback_execute' +} + +class ConnectionPromiseQueryPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:ConnectionPromise_query' +} + +class ConnectionPromiseExecutePlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:ConnectionPromise_execute' +} + +class PoolCallbackQueryPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:PoolCallback_query' +} + +class PoolCallbackExecutePlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:PoolCallback_execute' +} + +class PoolPromiseQueryPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:PoolPromise_query' +} + +class PoolPromiseExecutePlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:PoolPromise_execute' +} + +// ------------------------------------------------------------------------- +// v<3 query plugins — channels from thisPropertyName orchestrion entries +// ------------------------------------------------------------------------- + +class V2ConnectionQueryPromisePlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:v2Connection_queryPromise' +} + +class V2ConnectionQueryPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:v2Connection_query' +} + +class V2ConnectionQueryCallbackPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:v2Connection_queryCallback' +} + +class V2PoolBaseQueryPlugin extends MariadbQueryPlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:v2PoolBase_query' +} + +// ------------------------------------------------------------------------- +// PreparedStatement execute — statement.execute(values, [opts], [cb]) +// ctx.self is PrepareWrapper: .query = SQL, .conn.opts = connection options +// ------------------------------------------------------------------------- + +class PreparedStatementExecutePlugin extends MariadbQueryPlugin { + static id = 'mariadb' + + /** + * For prepared statement execution, connection options live on the + * internal connection object. Two layouts across v3 minor versions: + * v3.4.x+: ctx.self is PrepareWrapper → self.conn.opts + * v3.0.x: ctx.self is PrepareResultPacket → self.emitter.opts + * + * @param {object} self - PrepareWrapper or PrepareResultPacket (ctx.self) + * @returns {{ host?: string, user?: string, database?: string, port?: number }} + */ + getConf (self) { + const opts = self?.conn?.opts ?? self?.emitter?.opts + if (!opts) return {} + return { + host: opts.host, + user: opts.user, + database: opts.database, + port: opts.port, + } + } + + bindStart (ctx) { + const conf = this.getConf(ctx.self) + const sql = ctx.self?.query + const service = this.serviceName({ pluginConfig: this.config, dbConfig: conf, system: this.system }) + + const span = this.startSpan(this.operationName(), { + service, + resource: sql, + type: 'sql', + kind: 'client', + meta: { + 'db.type': this.system, + 'db.user': conf.user, + 'db.name': conf.database, + 'out.host': conf.host, + [CLIENT_PORT_KEY]: conf.port, + }, + }, ctx) + + ctx.sql = this.injectDbmQuery(span, sql, service) + return ctx.currentStore + } +} + +class PreparedStatementCallbackExecutePlugin extends PreparedStatementExecutePlugin { + static id = 'mariadb' + static prefix = 'tracing:orchestrion:mariadb:PrepareResultPacket_execute' +} + +module.exports = [ + ConnectionCallbackQueryPlugin, + ConnectionCallbackExecutePlugin, + ConnectionPromiseQueryPlugin, + ConnectionPromiseExecutePlugin, + PoolCallbackQueryPlugin, + PoolCallbackExecutePlugin, + PoolPromiseQueryPlugin, + PoolPromiseExecutePlugin, + V2ConnectionQueryPromisePlugin, + V2ConnectionQueryPlugin, + V2ConnectionQueryCallbackPlugin, + V2PoolBaseQueryPlugin, + PreparedStatementCallbackExecutePlugin, +] diff --git a/packages/datadog-plugin-mariadb/test/index.spec.js b/packages/datadog-plugin-mariadb/test/index.spec.js index 26e1ef53550..a40b94bdcca 100644 --- a/packages/datadog-plugin-mariadb/test/index.spec.js +++ b/packages/datadog-plugin-mariadb/test/index.spec.js @@ -30,6 +30,7 @@ describe('Plugin', () => { let connection afterEach((done) => { + if (!connection) return agent.close({ ritmReset: false }).then(done) connection.end(() => { agent.close({ ritmReset: false }).then(done) }) @@ -215,7 +216,7 @@ describe('Plugin', () => { let connection afterEach(async () => { - await connection.end() + if (connection) await connection.end() await agent.close({ ritmReset: false }) }) @@ -344,7 +345,7 @@ describe('Plugin', () => { }) it('should handle errors', async () => { - const queryPromise = connection.query('SELECT * FROM definitely_missing_table').catch(() => {}) + const queryPromise = connection.query('SELECT * FROM definitely_missing_table').catch(() => { }) await Promise.all([ agent.assertFirstTraceSpan({ @@ -368,7 +369,7 @@ describe('Plugin', () => { let connection afterEach(async () => { - await connection.end() + if (connection) await connection.end() await agent.close({ ritmReset: false }) }) @@ -409,6 +410,7 @@ describe('Plugin', () => { let mariadb afterEach((done) => { + if (!connection) return agent.close({ ritmReset: false }).then(done) connection.end(() => { agent.close({ ritmReset: false }).then(done) }) @@ -467,7 +469,7 @@ describe('Plugin', () => { let mariadb afterEach(async () => { - await connection.end() + if (connection) await connection.end() await agent.close({ ritmReset: false }) }) @@ -511,6 +513,7 @@ describe('Plugin', () => { let mariadb afterEach((done) => { + if (!connection) return agent.close({ ritmReset: false }).then(done) connection.end(() => { agent.close({ ritmReset: false }).then(done) }) @@ -538,7 +541,7 @@ describe('Plugin', () => { }) withNamingSchema( - () => connection.query('SELECT 1 + 1 AS solution', () => {}), + () => connection.query('SELECT 1 + 1 AS solution', () => { }), { v0: { opName: 'mariadb.query', @@ -562,7 +565,7 @@ describe('Plugin', () => { done() }) - connection.query('SELECT 1 + 1 AS solution', () => {}) + connection.query('SELECT 1 + 1 AS solution', () => { }) }) }) @@ -573,7 +576,7 @@ describe('Plugin', () => { let mariadb afterEach(async () => { - await connection.end() + if (connection) await connection.end() await agent.close({ ritmReset: false }) }) @@ -623,6 +626,7 @@ describe('Plugin', () => { let mariadb afterEach((done) => { + if (!pool) return agent.close({ ritmReset: false }).then(done) pool.end(() => { agent.close({ ritmReset: false }).then(done) }) @@ -691,7 +695,7 @@ describe('Plugin', () => { let mariadb afterEach(async () => { - await pool.end() + if (pool) await pool.end() await agent.close({ ritmReset: false }) }) @@ -755,6 +759,7 @@ describe('Plugin', () => { let mariadb afterEach((done) => { + if (!pool) return agent.close({ ritmReset: false }).then(done) pool.end(() => { agent.close({ ritmReset: false }).then(done) }) @@ -801,7 +806,7 @@ describe('Plugin', () => { let mariadb afterEach(async () => { - await pool.end() + if (pool) await pool.end() await agent.close({ ritmReset: false }) }) From 7dfab8149520d2d466a397621057974ba0d91d0e Mon Sep 17 00:00:00 2001 From: Crystal Luc-Magloire Date: Tue, 7 Apr 2026 15:24:37 -0400 Subject: [PATCH 2/4] refactor(mariadb): move instrumentation hooks from rewriter to orchestrion-config Replaces custom rewriter instrumentations/mariadb.js with entries in orchestrion-config/index.js using the standard YAML format. thisPropertyName is expressed as object_name/property_name per nodejs/orchestrion-js#58. noCallbackFallback entries are marked with TODO comments pending nodejs/orchestrion-js support. Reverts rewriter changes to transformer.js, transforms.js, instrumentations/index.js, and associated tests. --- .../rewriter/instrumentations/index.js | 1 - .../rewriter/instrumentations/mariadb.js | 350 ------------------ .../src/orchestrion-config/index.js | 241 ++++++++++++ .../test/helpers/rewriter/index.spec.js | 25 -- .../test-trace-this-property/index.js | 16 - .../test-trace-this-property/package.json | 1 - 6 files changed, 241 insertions(+), 393 deletions(-) delete mode 100644 packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js delete mode 100644 packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js delete mode 100644 packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json diff --git a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js index 9b917cf03a5..28b13f15191 100644 --- a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +++ b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js @@ -5,5 +5,4 @@ module.exports = [ ...require('./bullmq'), ...require('./langchain'), ...require('./langgraph'), - ...require('./mariadb'), ] diff --git a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js deleted file mode 100644 index c92d368a22a..00000000000 --- a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js +++ /dev/null @@ -1,350 +0,0 @@ -'use strict' - -module.exports = [ - // ------------------------------------------------------------------------- - // Orchestrion entries — these hooks are loaded via getHooks('mariadb') - // ------------------------------------------------------------------------- - // Pool.getConnection(callback) — callback-based; NOT returning a promise. - // Using kind: 'Callback' so the connection is available via ctx.result in - // asyncStart.runStores, enabling correct conf propagation and context restore. - { - module: { - name: 'mariadb', - versionRange: '>=3.4.1', - filePath: 'lib/pool.js', - }, - functionQuery: { - methodName: 'getConnection', - className: 'Pool', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'Pool_getConnection', - }, - // ------------------------------------------------------------------------- - // v2 constructor hooks — stash connection opts on the instance as __ddConf - // ------------------------------------------------------------------------- - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'lib/connection.js', - }, - functionQuery: { - functionName: 'Connection', - kind: 'Sync', - }, - channelName: 'v2Connection', - }, - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'lib/pool-base.js', - }, - functionQuery: { - functionName: 'PoolBase', - kind: 'Sync', - }, - channelName: 'v2PoolBase', - }, - - // ------------------------------------------------------------------------- - // v2 query hooks — use thisPropertyName to target arrow-function instance - // properties set inside function constructors. - // ------------------------------------------------------------------------- - - // v>=2.5.2 <3: _queryPromise (promise API) - { - module: { - name: 'mariadb', - versionRange: '>=2.5.2 <3', - filePath: 'lib/connection.js', - }, - functionQuery: { - thisPropertyName: '_queryPromise', - kind: 'Async', - }, - channelName: 'v2Connection_queryPromise', - }, - // v>=2.0.4 <=2.5.1: query (promise API) - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <=2.5.1', - filePath: 'lib/connection.js', - }, - functionQuery: { - thisPropertyName: 'query', - kind: 'Async', - }, - channelName: 'v2Connection_query', - }, - // All v2: _queryCallback (callback API) - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'lib/connection.js', - }, - functionQuery: { - thisPropertyName: '_queryCallback', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'v2Connection_queryCallback', - }, - // v2 pool: getConnection (promise API — context clearing + conf propagation) - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'lib/pool-base.js', - }, - functionQuery: { - thisPropertyName: 'getConnection', - kind: 'Async', - }, - channelName: 'v2PoolBase_getConnection', - }, - // v2 pool: query (promise API) - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'lib/pool-base.js', - }, - functionQuery: { - thisPropertyName: 'query', - kind: 'Async', - }, - channelName: 'v2PoolBase_query', - }, - - // ------------------------------------------------------------------------- - // v2 callback.js — createPool/createConnection hooks - // In v2, callback.js calls pool.initialize() right after construction, - // which creates the first TCP connection. Hooking createPool clears context - // so that initial connection doesn't become a child of the user's span. - // ------------------------------------------------------------------------- - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'callback.js', - }, - functionQuery: { - expressionName: 'createPool', - kind: 'Sync', - }, - channelName: 'createPool', - }, - { - module: { - name: 'mariadb', - versionRange: '>=2.0.4 <3', - filePath: 'callback.js', - }, - functionQuery: { - expressionName: 'createConnection', - kind: 'Sync', - }, - channelName: 'createConnection', - }, - - // ------------------------------------------------------------------------- - // Shimmer entries — v>=3 query/execute hooks that cannot use orchestrion - // because the AST rewriter moves the constructor body into a nested - // function, which breaks super() calls (SyntaxError). - // ------------------------------------------------------------------------- - - // callback.js — createConnection (sync wrapper to capture opts) - // Uses expressionName because these are named function expressions - // assigned to module.exports, not function declarations. - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'callback.js', - }, - functionQuery: { - expressionName: 'createConnection', - kind: 'Sync', - }, - channelName: 'createConnection', - }, - // callback.js — createPool (sync wrapper to capture opts) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'callback.js', - }, - functionQuery: { - expressionName: 'createPool', - kind: 'Sync', - }, - channelName: 'createPool', - }, - // promise.js — createConnection (async — returns promise) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'promise.js', - }, - functionQuery: { - expressionName: 'createConnection', - kind: 'Async', - }, - channelName: 'createConnection', - }, - // promise.js — createPool (sync wrapper to capture opts) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'promise.js', - }, - functionQuery: { - expressionName: 'createPool', - kind: 'Sync', - }, - channelName: 'createPool', - }, - // ConnectionCallback.prototype.query (callback-based — callback is last arg) - // noCallbackFallback: query() may be called without a callback (event-emitter usage) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/connection-callback.js', - }, - functionQuery: { - methodName: 'query', - className: 'ConnectionCallback', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'ConnectionCallback_query', - }, - // ConnectionCallback.prototype.execute (callback-based — callback is last arg) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/connection-callback.js', - }, - functionQuery: { - methodName: 'execute', - className: 'ConnectionCallback', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'ConnectionCallback_execute', - }, - // ConnectionPromise.prototype.query (promise-based) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/connection-promise.js', - }, - functionQuery: { - methodName: 'query', - className: 'ConnectionPromise', - kind: 'Async', - }, - channelName: 'ConnectionPromise_query', - }, - // ConnectionPromise.prototype.execute (promise-based) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/connection-promise.js', - }, - functionQuery: { - methodName: 'execute', - className: 'ConnectionPromise', - kind: 'Async', - }, - channelName: 'ConnectionPromise_execute', - }, - // PoolCallback.prototype.query (callback-based — callback is last arg) - // noCallbackFallback: pool.query() is often called without a callback (fire-and-forget) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/pool-callback.js', - }, - functionQuery: { - methodName: 'query', - className: 'PoolCallback', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'PoolCallback_query', - }, - // PoolCallback.prototype.execute (callback-based — callback is last arg) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/pool-callback.js', - }, - functionQuery: { - methodName: 'execute', - className: 'PoolCallback', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'PoolCallback_execute', - }, - // PoolPromise.prototype.query (promise-based) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/pool-promise.js', - }, - functionQuery: { - methodName: 'query', - className: 'PoolPromise', - kind: 'Async', - }, - channelName: 'PoolPromise_query', - }, - // PoolPromise.prototype.execute (promise-based) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/pool-promise.js', - }, - functionQuery: { - methodName: 'execute', - className: 'PoolPromise', - kind: 'Async', - }, - channelName: 'PoolPromise_execute', - }, - // PrepareResultPacket.prototype.execute — used via PrepareWrapper (statement.execute) - // ctx.self is PrepareWrapper: .query = SQL, .conn.opts = connection options - // noCallbackFallback: execute() may be called without callback (promise mode) - { - module: { - name: 'mariadb', - versionRange: '>=3', - filePath: 'lib/cmd/class/prepare-result-packet.js', - }, - functionQuery: { - methodName: 'execute', - className: 'PrepareResultPacket', - kind: 'Callback', - noCallbackFallback: true, - }, - channelName: 'PrepareResultPacket_execute', - }, -] diff --git a/packages/datadog-instrumentations/src/orchestrion-config/index.js b/packages/datadog-instrumentations/src/orchestrion-config/index.js index 4692714a0ee..b1a16ae9c66 100644 --- a/packages/datadog-instrumentations/src/orchestrion-config/index.js +++ b/packages/datadog-instrumentations/src/orchestrion-config/index.js @@ -83,4 +83,245 @@ instrumentations: class: VectorStore operator: tracePromise channel_name: "VectorStore_similaritySearchWithScore" + # --------------------------------------------------------------------------- + # mariadb + # --------------------------------------------------------------------------- + # v3: Pool.getConnection (callback-based) + # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) + - module_name: mariadb + version_range: ">=3.4.1" + file_path: lib/pool.js + function_query: + name: getConnection + type: method + kind: callback + class: Pool + operator: traceCallback + channel_name: "Pool_getConnection" + # v2: Connection constructor (sync — stash opts on instance as __ddConf) + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: lib/connection.js + function_query: + name: Connection + type: function + kind: sync + operator: traceSync + channel_name: "v2Connection" + # v2: PoolBase constructor (sync — stash opts on instance as __ddConf) + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: lib/pool-base.js + function_query: + name: PoolBase + type: function + kind: sync + operator: traceSync + channel_name: "v2PoolBase" + # v>=2.5.2 <3: _queryPromise (promise API — arrow fn assigned to this in constructor) + - module_name: mariadb + version_range: ">=2.5.2 <3" + file_path: lib/connection.js + function_query: + object_name: this + property_name: _queryPromise + kind: async + operator: tracePromise + channel_name: "v2Connection_queryPromise" + # v>=2.0.4 <=2.5.1: query (promise API — arrow fn assigned to this in constructor) + - module_name: mariadb + version_range: ">=2.0.4 <=2.5.1" + file_path: lib/connection.js + function_query: + object_name: this + property_name: query + kind: async + operator: tracePromise + channel_name: "v2Connection_query" + # v2: _queryCallback (callback API — arrow fn assigned to this in constructor) + # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: lib/connection.js + function_query: + object_name: this + property_name: _queryCallback + kind: callback + operator: traceCallback + channel_name: "v2Connection_queryCallback" + # v2: pool getConnection (promise API — arrow fn assigned to this in constructor) + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: lib/pool-base.js + function_query: + object_name: this + property_name: getConnection + kind: async + operator: tracePromise + channel_name: "v2PoolBase_getConnection" + # v2: pool query (promise API — arrow fn assigned to this in constructor) + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: lib/pool-base.js + function_query: + object_name: this + property_name: query + kind: async + operator: tracePromise + channel_name: "v2PoolBase_query" + # v2: callback.js createPool/createConnection (sync — clear context before pool init) + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: callback.js + function_query: + name: createPool + type: expression + kind: sync + operator: traceSync + channel_name: "createPool" + - module_name: mariadb + version_range: ">=2.0.4 <3" + file_path: callback.js + function_query: + name: createConnection + type: expression + kind: sync + operator: traceSync + channel_name: "createConnection" + # v3: callback.js createPool/createConnection + - module_name: mariadb + version_range: ">=3" + file_path: callback.js + function_query: + name: createPool + type: expression + kind: sync + operator: traceSync + channel_name: "createPool" + - module_name: mariadb + version_range: ">=3" + file_path: callback.js + function_query: + name: createConnection + type: expression + kind: sync + operator: traceSync + channel_name: "createConnection" + # v3: promise.js createPool/createConnection + - module_name: mariadb + version_range: ">=3" + file_path: promise.js + function_query: + name: createConnection + type: expression + kind: async + operator: tracePromise + channel_name: "createConnection" + - module_name: mariadb + version_range: ">=3" + file_path: promise.js + function_query: + name: createPool + type: expression + kind: sync + operator: traceSync + channel_name: "createPool" + # v3: ConnectionCallback query/execute (callback-based) + # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) + - module_name: mariadb + version_range: ">=3" + file_path: lib/connection-callback.js + function_query: + name: query + type: method + kind: callback + class: ConnectionCallback + operator: traceCallback + channel_name: "ConnectionCallback_query" + - module_name: mariadb + version_range: ">=3" + file_path: lib/connection-callback.js + function_query: + name: execute + type: method + kind: callback + class: ConnectionCallback + operator: traceCallback + channel_name: "ConnectionCallback_execute" + # v3: ConnectionPromise query/execute (promise-based) + - module_name: mariadb + version_range: ">=3" + file_path: lib/connection-promise.js + function_query: + name: query + type: method + kind: async + class: ConnectionPromise + operator: tracePromise + channel_name: "ConnectionPromise_query" + - module_name: mariadb + version_range: ">=3" + file_path: lib/connection-promise.js + function_query: + name: execute + type: method + kind: async + class: ConnectionPromise + operator: tracePromise + channel_name: "ConnectionPromise_execute" + # v3: PoolCallback query/execute (callback-based) + # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) + - module_name: mariadb + version_range: ">=3" + file_path: lib/pool-callback.js + function_query: + name: query + type: method + kind: callback + class: PoolCallback + operator: traceCallback + channel_name: "PoolCallback_query" + - module_name: mariadb + version_range: ">=3" + file_path: lib/pool-callback.js + function_query: + name: execute + type: method + kind: callback + class: PoolCallback + operator: traceCallback + channel_name: "PoolCallback_execute" + # v3: PoolPromise query/execute (promise-based) + - module_name: mariadb + version_range: ">=3" + file_path: lib/pool-promise.js + function_query: + name: query + type: method + kind: async + class: PoolPromise + operator: tracePromise + channel_name: "PoolPromise_query" + - module_name: mariadb + version_range: ">=3" + file_path: lib/pool-promise.js + function_query: + name: execute + type: method + kind: async + class: PoolPromise + operator: tracePromise + channel_name: "PoolPromise_execute" + # v3: PrepareResultPacket.execute (callback-based, used via PrepareWrapper) + # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) + - module_name: mariadb + version_range: ">=3" + file_path: lib/cmd/class/prepare-result-packet.js + function_query: + name: execute + type: method + kind: callback + class: PrepareResultPacket + operator: traceCallback + channel_name: "PrepareResultPacket_execute" ` diff --git a/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js b/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js index 8928e0f4ed4..f95e8b3c937 100644 --- a/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js +++ b/packages/datadog-instrumentations/test/helpers/rewriter/index.spec.js @@ -261,18 +261,6 @@ describe('check-require-cache', () => { }, channelName: 'trace_class_private_method', }, - { - module: { - name: 'test-trace-this-property', - versionRange: '>=0.1', - filePath: 'index.js', - }, - functionQuery: { - thisPropertyName: 'test', - kind: 'Sync', - }, - channelName: 'test_invoke', - }, ], }) }) @@ -527,17 +515,4 @@ describe('check-require-cache', () => { assert.ok(subs.start.called) }) - - it('should auto instrument arrow function instance properties (thisPropertyName)', done => { - const { test } = compile('test-trace-this-property') - - subs = { - start: () => setImmediate(done), - } - - ch = tracingChannel('orchestrion:test-trace-this-property:test_invoke') - ch.subscribe(subs) - - test() - }) }) diff --git a/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js b/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js deleted file mode 100644 index e48ddd58433..00000000000 --- a/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/index.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -// Simulates old-style function constructor where a query method is -// assigned as an arrow-function instance property (as in mariadb v2). -function Foo (opts) { - this.opts = opts - - this.test = () => {} -} - -function test () { - const foo = new Foo({ host: 'localhost' }) - foo.test('SELECT 1') -} - -module.exports = { test } diff --git a/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json b/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json deleted file mode 100644 index 612148f7f42..00000000000 --- a/packages/datadog-instrumentations/test/helpers/rewriter/node_modules/test-trace-this-property/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "name": "test-trace-this-property", "version": "1.0.0" } From b0f2a1e0d055ee588937b24e23053649443763d7 Mon Sep 17 00:00:00 2001 From: Crystal Luc-Magloire Date: Tue, 7 Apr 2026 16:35:42 -0400 Subject: [PATCH 3/4] fix(mariadb): remove mariadb hooks from orchestrion-config orchestrion-config/index.js is consumed by the AppSec IAST wasm rewriter, not the APM orchestrion system. The mariadb entries broke AppSec tests because the wasm rewriter does not support kind: callback. --- .../src/orchestrion-config/index.js | 241 ------------------ 1 file changed, 241 deletions(-) diff --git a/packages/datadog-instrumentations/src/orchestrion-config/index.js b/packages/datadog-instrumentations/src/orchestrion-config/index.js index b1a16ae9c66..4692714a0ee 100644 --- a/packages/datadog-instrumentations/src/orchestrion-config/index.js +++ b/packages/datadog-instrumentations/src/orchestrion-config/index.js @@ -83,245 +83,4 @@ instrumentations: class: VectorStore operator: tracePromise channel_name: "VectorStore_similaritySearchWithScore" - # --------------------------------------------------------------------------- - # mariadb - # --------------------------------------------------------------------------- - # v3: Pool.getConnection (callback-based) - # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) - - module_name: mariadb - version_range: ">=3.4.1" - file_path: lib/pool.js - function_query: - name: getConnection - type: method - kind: callback - class: Pool - operator: traceCallback - channel_name: "Pool_getConnection" - # v2: Connection constructor (sync — stash opts on instance as __ddConf) - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: lib/connection.js - function_query: - name: Connection - type: function - kind: sync - operator: traceSync - channel_name: "v2Connection" - # v2: PoolBase constructor (sync — stash opts on instance as __ddConf) - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: lib/pool-base.js - function_query: - name: PoolBase - type: function - kind: sync - operator: traceSync - channel_name: "v2PoolBase" - # v>=2.5.2 <3: _queryPromise (promise API — arrow fn assigned to this in constructor) - - module_name: mariadb - version_range: ">=2.5.2 <3" - file_path: lib/connection.js - function_query: - object_name: this - property_name: _queryPromise - kind: async - operator: tracePromise - channel_name: "v2Connection_queryPromise" - # v>=2.0.4 <=2.5.1: query (promise API — arrow fn assigned to this in constructor) - - module_name: mariadb - version_range: ">=2.0.4 <=2.5.1" - file_path: lib/connection.js - function_query: - object_name: this - property_name: query - kind: async - operator: tracePromise - channel_name: "v2Connection_query" - # v2: _queryCallback (callback API — arrow fn assigned to this in constructor) - # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: lib/connection.js - function_query: - object_name: this - property_name: _queryCallback - kind: callback - operator: traceCallback - channel_name: "v2Connection_queryCallback" - # v2: pool getConnection (promise API — arrow fn assigned to this in constructor) - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: lib/pool-base.js - function_query: - object_name: this - property_name: getConnection - kind: async - operator: tracePromise - channel_name: "v2PoolBase_getConnection" - # v2: pool query (promise API — arrow fn assigned to this in constructor) - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: lib/pool-base.js - function_query: - object_name: this - property_name: query - kind: async - operator: tracePromise - channel_name: "v2PoolBase_query" - # v2: callback.js createPool/createConnection (sync — clear context before pool init) - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: callback.js - function_query: - name: createPool - type: expression - kind: sync - operator: traceSync - channel_name: "createPool" - - module_name: mariadb - version_range: ">=2.0.4 <3" - file_path: callback.js - function_query: - name: createConnection - type: expression - kind: sync - operator: traceSync - channel_name: "createConnection" - # v3: callback.js createPool/createConnection - - module_name: mariadb - version_range: ">=3" - file_path: callback.js - function_query: - name: createPool - type: expression - kind: sync - operator: traceSync - channel_name: "createPool" - - module_name: mariadb - version_range: ">=3" - file_path: callback.js - function_query: - name: createConnection - type: expression - kind: sync - operator: traceSync - channel_name: "createConnection" - # v3: promise.js createPool/createConnection - - module_name: mariadb - version_range: ">=3" - file_path: promise.js - function_query: - name: createConnection - type: expression - kind: async - operator: tracePromise - channel_name: "createConnection" - - module_name: mariadb - version_range: ">=3" - file_path: promise.js - function_query: - name: createPool - type: expression - kind: sync - operator: traceSync - channel_name: "createPool" - # v3: ConnectionCallback query/execute (callback-based) - # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) - - module_name: mariadb - version_range: ">=3" - file_path: lib/connection-callback.js - function_query: - name: query - type: method - kind: callback - class: ConnectionCallback - operator: traceCallback - channel_name: "ConnectionCallback_query" - - module_name: mariadb - version_range: ">=3" - file_path: lib/connection-callback.js - function_query: - name: execute - type: method - kind: callback - class: ConnectionCallback - operator: traceCallback - channel_name: "ConnectionCallback_execute" - # v3: ConnectionPromise query/execute (promise-based) - - module_name: mariadb - version_range: ">=3" - file_path: lib/connection-promise.js - function_query: - name: query - type: method - kind: async - class: ConnectionPromise - operator: tracePromise - channel_name: "ConnectionPromise_query" - - module_name: mariadb - version_range: ">=3" - file_path: lib/connection-promise.js - function_query: - name: execute - type: method - kind: async - class: ConnectionPromise - operator: tracePromise - channel_name: "ConnectionPromise_execute" - # v3: PoolCallback query/execute (callback-based) - # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) - - module_name: mariadb - version_range: ">=3" - file_path: lib/pool-callback.js - function_query: - name: query - type: method - kind: callback - class: PoolCallback - operator: traceCallback - channel_name: "PoolCallback_query" - - module_name: mariadb - version_range: ">=3" - file_path: lib/pool-callback.js - function_query: - name: execute - type: method - kind: callback - class: PoolCallback - operator: traceCallback - channel_name: "PoolCallback_execute" - # v3: PoolPromise query/execute (promise-based) - - module_name: mariadb - version_range: ">=3" - file_path: lib/pool-promise.js - function_query: - name: query - type: method - kind: async - class: PoolPromise - operator: tracePromise - channel_name: "PoolPromise_query" - - module_name: mariadb - version_range: ">=3" - file_path: lib/pool-promise.js - function_query: - name: execute - type: method - kind: async - class: PoolPromise - operator: tracePromise - channel_name: "PoolPromise_execute" - # v3: PrepareResultPacket.execute (callback-based, used via PrepareWrapper) - # TODO: noCallbackFallback not yet supported in orchestrion-config (nodejs/orchestrion-js pending) - - module_name: mariadb - version_range: ">=3" - file_path: lib/cmd/class/prepare-result-packet.js - function_query: - name: execute - type: method - kind: callback - class: PrepareResultPacket - operator: traceCallback - channel_name: "PrepareResultPacket_execute" ` From ac1a0896186eda8138239badfc7c0cb0be4f97bb Mon Sep 17 00:00:00 2001 From: Crystal Luc-Magloire Date: Tue, 7 Apr 2026 16:57:49 -0400 Subject: [PATCH 4/4] feat(mariadb): add orchestrion rewriter instrumentation entries Adds mariadb hook definitions to the rewriter instrumentations for v2 and v3+, covering createConnection, createPool, Pool.getConnection, and all query/execute methods across callback and promise APIs. Co-Authored-By: Claude Sonnet 4.6 --- .../rewriter/instrumentations/index.js | 1 + .../rewriter/instrumentations/mariadb.js | 350 ++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js diff --git a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js index 28b13f15191..9b917cf03a5 100644 --- a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +++ b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js @@ -5,4 +5,5 @@ module.exports = [ ...require('./bullmq'), ...require('./langchain'), ...require('./langgraph'), + ...require('./mariadb'), ] diff --git a/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js new file mode 100644 index 00000000000..c92d368a22a --- /dev/null +++ b/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mariadb.js @@ -0,0 +1,350 @@ +'use strict' + +module.exports = [ + // ------------------------------------------------------------------------- + // Orchestrion entries — these hooks are loaded via getHooks('mariadb') + // ------------------------------------------------------------------------- + // Pool.getConnection(callback) — callback-based; NOT returning a promise. + // Using kind: 'Callback' so the connection is available via ctx.result in + // asyncStart.runStores, enabling correct conf propagation and context restore. + { + module: { + name: 'mariadb', + versionRange: '>=3.4.1', + filePath: 'lib/pool.js', + }, + functionQuery: { + methodName: 'getConnection', + className: 'Pool', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'Pool_getConnection', + }, + // ------------------------------------------------------------------------- + // v2 constructor hooks — stash connection opts on the instance as __ddConf + // ------------------------------------------------------------------------- + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/connection.js', + }, + functionQuery: { + functionName: 'Connection', + kind: 'Sync', + }, + channelName: 'v2Connection', + }, + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/pool-base.js', + }, + functionQuery: { + functionName: 'PoolBase', + kind: 'Sync', + }, + channelName: 'v2PoolBase', + }, + + // ------------------------------------------------------------------------- + // v2 query hooks — use thisPropertyName to target arrow-function instance + // properties set inside function constructors. + // ------------------------------------------------------------------------- + + // v>=2.5.2 <3: _queryPromise (promise API) + { + module: { + name: 'mariadb', + versionRange: '>=2.5.2 <3', + filePath: 'lib/connection.js', + }, + functionQuery: { + thisPropertyName: '_queryPromise', + kind: 'Async', + }, + channelName: 'v2Connection_queryPromise', + }, + // v>=2.0.4 <=2.5.1: query (promise API) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <=2.5.1', + filePath: 'lib/connection.js', + }, + functionQuery: { + thisPropertyName: 'query', + kind: 'Async', + }, + channelName: 'v2Connection_query', + }, + // All v2: _queryCallback (callback API) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/connection.js', + }, + functionQuery: { + thisPropertyName: '_queryCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'v2Connection_queryCallback', + }, + // v2 pool: getConnection (promise API — context clearing + conf propagation) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/pool-base.js', + }, + functionQuery: { + thisPropertyName: 'getConnection', + kind: 'Async', + }, + channelName: 'v2PoolBase_getConnection', + }, + // v2 pool: query (promise API) + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'lib/pool-base.js', + }, + functionQuery: { + thisPropertyName: 'query', + kind: 'Async', + }, + channelName: 'v2PoolBase_query', + }, + + // ------------------------------------------------------------------------- + // v2 callback.js — createPool/createConnection hooks + // In v2, callback.js calls pool.initialize() right after construction, + // which creates the first TCP connection. Hooking createPool clears context + // so that initial connection doesn't become a child of the user's span. + // ------------------------------------------------------------------------- + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createPool', + kind: 'Sync', + }, + channelName: 'createPool', + }, + { + module: { + name: 'mariadb', + versionRange: '>=2.0.4 <3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createConnection', + kind: 'Sync', + }, + channelName: 'createConnection', + }, + + // ------------------------------------------------------------------------- + // Shimmer entries — v>=3 query/execute hooks that cannot use orchestrion + // because the AST rewriter moves the constructor body into a nested + // function, which breaks super() calls (SyntaxError). + // ------------------------------------------------------------------------- + + // callback.js — createConnection (sync wrapper to capture opts) + // Uses expressionName because these are named function expressions + // assigned to module.exports, not function declarations. + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createConnection', + kind: 'Sync', + }, + channelName: 'createConnection', + }, + // callback.js — createPool (sync wrapper to capture opts) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'callback.js', + }, + functionQuery: { + expressionName: 'createPool', + kind: 'Sync', + }, + channelName: 'createPool', + }, + // promise.js — createConnection (async — returns promise) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'promise.js', + }, + functionQuery: { + expressionName: 'createConnection', + kind: 'Async', + }, + channelName: 'createConnection', + }, + // promise.js — createPool (sync wrapper to capture opts) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'promise.js', + }, + functionQuery: { + expressionName: 'createPool', + kind: 'Sync', + }, + channelName: 'createPool', + }, + // ConnectionCallback.prototype.query (callback-based — callback is last arg) + // noCallbackFallback: query() may be called without a callback (event-emitter usage) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-callback.js', + }, + functionQuery: { + methodName: 'query', + className: 'ConnectionCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'ConnectionCallback_query', + }, + // ConnectionCallback.prototype.execute (callback-based — callback is last arg) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-callback.js', + }, + functionQuery: { + methodName: 'execute', + className: 'ConnectionCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'ConnectionCallback_execute', + }, + // ConnectionPromise.prototype.query (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-promise.js', + }, + functionQuery: { + methodName: 'query', + className: 'ConnectionPromise', + kind: 'Async', + }, + channelName: 'ConnectionPromise_query', + }, + // ConnectionPromise.prototype.execute (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/connection-promise.js', + }, + functionQuery: { + methodName: 'execute', + className: 'ConnectionPromise', + kind: 'Async', + }, + channelName: 'ConnectionPromise_execute', + }, + // PoolCallback.prototype.query (callback-based — callback is last arg) + // noCallbackFallback: pool.query() is often called without a callback (fire-and-forget) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-callback.js', + }, + functionQuery: { + methodName: 'query', + className: 'PoolCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'PoolCallback_query', + }, + // PoolCallback.prototype.execute (callback-based — callback is last arg) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-callback.js', + }, + functionQuery: { + methodName: 'execute', + className: 'PoolCallback', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'PoolCallback_execute', + }, + // PoolPromise.prototype.query (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-promise.js', + }, + functionQuery: { + methodName: 'query', + className: 'PoolPromise', + kind: 'Async', + }, + channelName: 'PoolPromise_query', + }, + // PoolPromise.prototype.execute (promise-based) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/pool-promise.js', + }, + functionQuery: { + methodName: 'execute', + className: 'PoolPromise', + kind: 'Async', + }, + channelName: 'PoolPromise_execute', + }, + // PrepareResultPacket.prototype.execute — used via PrepareWrapper (statement.execute) + // ctx.self is PrepareWrapper: .query = SQL, .conn.opts = connection options + // noCallbackFallback: execute() may be called without callback (promise mode) + { + module: { + name: 'mariadb', + versionRange: '>=3', + filePath: 'lib/cmd/class/prepare-result-packet.js', + }, + functionQuery: { + methodName: 'execute', + className: 'PrepareResultPacket', + kind: 'Callback', + noCallbackFallback: true, + }, + channelName: 'PrepareResultPacket_execute', + }, +]