Merge branch '2.3' into 2.4

This commit is contained in:
Markus Mäkelä
2019-10-10 21:42:43 +03:00
12 changed files with 854 additions and 231 deletions

View File

@ -64,4 +64,5 @@ private:
std::vector<std::vector<std::string>> m_rows; std::vector<std::vector<std::string>> m_rows;
ResultSet(std::initializer_list<std::string> names); ResultSet(std::initializer_list<std::string> names);
json_t* get_json_value(const std::string& s);
}; };

View File

@ -6,7 +6,10 @@ if (BUILD_MAXCTRL)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib/version.js.in ${CMAKE_CURRENT_BINARY_DIR}/lib/version.js @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib/version.js.in ${CMAKE_CURRENT_BINARY_DIR}/lib/version.js @ONLY)
file(GLOB_RECURSE MAXCTRL_SOURCES lib/*)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/maxctrl/maxctrl add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/maxctrl/maxctrl
DEPENDS ${MAXCTRL_SOURCES} maxctrl.js
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_SOURCE_DIR} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_custom_target(maxctrl ALL DEPENDS ${CMAKE_BINARY_DIR}/maxctrl/maxctrl) add_custom_target(maxctrl ALL DEPENDS ${CMAKE_BINARY_DIR}/maxctrl/maxctrl)

View File

@ -112,7 +112,7 @@ module.exports = function() {
row = [] row = []
fields.forEach(function(p) { fields.forEach(function(p) {
var v = _.getPath(i, p[Object.keys(p)[0]], '') var v = _.getPath(i, p.path, '')
if (Array.isArray(v)) { if (Array.isArray(v)) {
v = v.join(', ') v = v.join(', ')
@ -162,7 +162,7 @@ module.exports = function() {
var header = [] var header = []
fields.forEach(function(i) { fields.forEach(function(i) {
header.push(Object.keys(i)) header.push(i.name)
}) })
var table = getTable(header) var table = getTable(header)
@ -188,7 +188,7 @@ module.exports = function() {
var header = [] var header = []
fields.forEach(function(i) { fields.forEach(function(i) {
header.push(Object.keys(i)) header.push(i.name)
}) })
var table = getTable(header) var table = getTable(header)
@ -197,7 +197,7 @@ module.exports = function() {
row = [] row = []
fields.forEach(function(p) { fields.forEach(function(p) {
var v = _.getPath(i, p[Object.keys(p)[0]], '') var v = _.getPath(i, p[p.name], '')
if (Array.isArray(v) && typeof(v[0]) != 'object') { if (Array.isArray(v) && typeof(v[0]) != 'object') {
v = v.join(', ') v = v.join(', ')
@ -229,7 +229,7 @@ module.exports = function() {
separator = '\n' separator = '\n'
var max_field_length = 0 var max_field_length = 0
fields.forEach(function (i) { fields.forEach(function (i) {
var k = Object.keys(i)[0] var k = i.name
if (k.length > max_field_length) { if (k.length > max_field_length) {
max_field_length = k.length max_field_length = k.length
} }
@ -244,8 +244,8 @@ module.exports = function() {
} }
fields.forEach(function(i) { fields.forEach(function(i) {
var k = Object.keys(i)[0] var k = i.name
var path = i[k] var path = i.path
var v = _.getPath(data, path, '') var v = _.getPath(data, path, '')
if (Array.isArray(v) && typeof(v[0]) != 'object') { if (Array.isArray(v) && typeof(v[0]) != 'object') {
@ -422,6 +422,24 @@ module.exports = function() {
default: false default: false
} }
} }
this.fieldDescriptions = function(fields) {
var t = new Table({chars: {
'top' : '', 'top-mid': '', 'top-left': '', 'top-right': '', 'left': ' ', 'right': '',
'left-mid': '' , 'mid': '' , 'mid-mid': '', 'right-mid': '' , 'middle': '',
'bottom' : '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',
}})
t.push(['Field', 'Description'])
t.push(['-----', '-----------'])
for (f of fields) {
t.push([f.name, f.description])
}
return '\n\n' + t.toString()
}
} }
@ -441,8 +459,6 @@ var tsvopts = {
'padding-right': 0, 'padding-right': 0,
compact: true compact: true
}, },
} }
function getList() { function getList() {

View File

@ -13,24 +13,252 @@
require('./common.js')() require('./common.js')()
const list_servers_fields = [
{
name: 'Server',
path: 'id',
description: 'Server name'
},
{
name: 'Address',
path: 'attributes.parameters.address',
description: 'Address where the server listens'
},
{
name: 'Port',
path: 'attributes.parameters.port',
description: 'The port on which the server listens'
},
{
name: 'Connections',
path: 'attributes.statistics.connections',
description: 'Current connection count'
},
{
name: 'State',
path: 'attributes.state',
description: 'Server state'
},
{
name: 'GTID',
path: 'attributes.gtid_current_pos',
description: 'Current value of @@gtid_current_pos'
}
]
const list_services_fields = [
{
name: 'Service',
path: 'id',
description: 'Service name'
},
{
name: 'Router',
path: 'attributes.router',
description: 'Router used by the service'
},
{
name: 'Connections',
path: 'attributes.connections',
description: 'Current connection count'
},
{
name: 'Total Connections',
path: 'attributes.total_connections',
description: 'Total connection count'
},
{
name: 'Servers',
path: 'relationships.servers.data[].id',
description: 'Servers that the service uses'
}
]
const list_listeners_fields = [
{
name: 'Name',
path: 'id',
description: 'Listener name'
},
{
name: 'Port',
path: 'attributes.parameters.port',
description: 'The port where the listener listens'
},
{
name: 'Host',
path: 'attributes.parameters.host',
description: 'The address or socket where the listener listens'
},
{
name: 'State',
path: 'attributes.state',
description: 'Listener state'
}
]
const list_monitors_fields = [
{
name: 'Monitor',
path: 'id',
description: 'Monitor name'
},
{
name: 'State',
path: 'attributes.state',
description: 'Monitor state'
},
{
name: 'Servers',
path: 'relationships.servers.data[].id',
description: 'The servers that this monitor monitors'
}
]
const list_sessions_fields = [
{
name: 'Id',
path: 'id',
description: 'Session ID'
},
{
name: 'User',
path: 'attributes.user',
description: 'Username'
},
{
name: 'Host',
path: 'attributes.remote',
description: 'Client host address'
},
{
name: 'Connected',
path: 'attributes.connected',
description: 'Time when the session started'
},
{
name: 'Idle',
path: 'attributes.idle',
description: 'How long the session has been idle, in seconds'
},
{
name: 'Service',
path: 'relationships.services.data[].id',
description: 'The service where the session connected'
}
]
const list_filters_fields = [
{
name: 'Filter',
path: 'id',
description: 'Filter name'
},
{
name: 'Service',
path: 'relationships.services.data[].id',
description: 'Services that use the filter'
},
{
name: 'Module',
path: 'attributes.module',
description: 'The module that the filter uses'
}
]
const list_modules_fields = [
{
name: 'Module',
path: 'id',
description: 'Module name'
},
{
name: 'Type',
path: 'attributes.module_type',
description: 'Module type'
},
{
name: 'Version',
path: 'attributes.version',
description: 'Module version'
}
]
const list_threads_fields = [
{
name: 'Id',
path: 'id',
description: 'Thread ID'
},
{
name: 'Current FDs',
path: 'attributes.stats.current_descriptors',
description: 'Current number of managed file descriptors'
},
{
name: 'Total FDs',
path: 'attributes.stats.total_descriptors',
description: 'Total number of managed file descriptors'
},
{
name: 'Load (1s)',
path: 'attributes.stats.load.last_second',
description: 'Load percentage over the last second'
},
{
name: 'Load (1m)',
path: 'attributes.stats.load.last_minute',
description: 'Load percentage over the last minute'
},
{
name: 'Load (1h)',
path: 'attributes.stats.load.last_hour',
description: 'Load percentage over the last hour'
}
]
const list_users_fields = [
{
name: 'Name',
path: 'id',
description: 'User name'
},
{
name: 'Type',
path: 'type',
description: 'User type'
},
{
name: 'Privileges',
path: 'attributes.account',
description: 'User privileges'
},
]
const list_commands_fields = [
{
name: 'Module',
path: 'id',
description: 'Module name'
},
{
name: 'Commands',
path: 'attributes.commands[].id',
description: 'Available commands'
}
]
exports.command = 'list <command>' exports.command = 'list <command>'
exports.desc = 'List objects' exports.desc = 'List objects'
exports.handler = function() {} exports.handler = function() {}
exports.builder = function(yargs) { exports.builder = function(yargs) {
yargs yargs
.command('servers', 'List servers', function(yargs) { .command('servers', 'List servers', function(yargs) {
return yargs.epilog('List all servers in MaxScale.') return yargs.epilog('List all servers in MaxScale.' +
fieldDescriptions(list_servers_fields))
.usage('Usage: list servers') .usage('Usage: list servers')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
fields = [
{'Server': 'id'},
{'Address': 'attributes.parameters.address'},
{'Port': 'attributes.parameters.port'},
{'Connections': 'attributes.statistics.connections'},
{'State': 'attributes.state'},
{'GTID': 'attributes.gtid_current_pos'}
]
// First, get the list of all servers // First, get the list of all servers
return getJson(host, 'servers') return getJson(host, 'servers')
@ -76,52 +304,41 @@ exports.builder = function(yargs) {
} }
}) })
}) })
.then(() => filterResource(res, fields)) .then(() => filterResource(res, list_servers_fields))
.then((res) => rawCollectionAsTable(res, fields)) .then((res) => rawCollectionAsTable(res, list_servers_fields))
}) })
}) })
}) })
.command('services', 'List services', function(yargs) { .command('services', 'List services', function(yargs) {
return yargs.epilog('List all services and the servers they use.') return yargs.epilog('List all services and the servers they use.' +
fieldDescriptions(list_services_fields))
.usage('Usage: list services') .usage('Usage: list services')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'services',[ return getCollection(host, 'services', list_services_fields)
{'Service': 'id'},
{'Router': 'attributes.router'},
{'Connections': 'attributes.connections'},
{'Total Connections': 'attributes.total_connections'},
{'Servers': 'relationships.servers.data[].id'}
])
}) })
}) })
.command('listeners <service>', 'List listeners of a service', function(yargs) { .command('listeners <service>', 'List listeners of a service', function(yargs) {
return yargs.epilog('List listeners for a service.') return yargs.epilog('List listeners for a service.' +
fieldDescriptions(list_listeners_fields))
.usage('Usage: list listeners <service>') .usage('Usage: list listeners <service>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getSubCollection(host, 'services/' + argv.service, 'attributes.listeners', [ return getSubCollection(host, 'services/' + argv.service, 'attributes.listeners', list_listeners_fields)
{'Name': 'id'},
{'Port': 'attributes.parameters.port'},
{'Host': 'attributes.parameters.host'},
{'State': 'attributes.state'}
])
}) })
}) })
.command('monitors', 'List monitors', function(yargs) { .command('monitors', 'List monitors', function(yargs) {
return yargs.epilog('List all monitors in MaxScale.') return yargs.epilog('List all monitors in MaxScale.' +
fieldDescriptions(list_monitors_fields))
.usage('Usage: list monitors') .usage('Usage: list monitors')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'monitors', [ return getCollection(host, 'monitors', list_monitors_fields)
{'Monitor': 'id'},
{'State': 'attributes.state'},
{'Servers': 'relationships.servers.data[].id'}
])
}) })
}) })
.command('sessions', 'List sessions', function(yargs) { .command('sessions', 'List sessions', function(yargs) {
return yargs.epilog('List all client sessions.') return yargs.epilog('List all client sessions.' +
fieldDescriptions(list_sessions_fields))
.usage('Usage: list sessions') .usage('Usage: list sessions')
.group([rDnsOption.shortname], 'Options:') .group([rDnsOption.shortname], 'Options:')
.option(rDnsOption.shortname, rDnsOption.definition) .option(rDnsOption.shortname, rDnsOption.definition)
@ -131,76 +348,53 @@ exports.builder = function(yargs) {
if (argv[this.rDnsOption.shortname]) { if (argv[this.rDnsOption.shortname]) {
resource += '?' + this.rDnsOption.optionOn resource += '?' + this.rDnsOption.optionOn
} }
return getCollection(host, resource,[ return getCollection(host, 'sessions', list_sessions_fields)
{'Id': 'id'},
{'User': 'attributes.user'},
{'Host': 'attributes.remote'},
{'Connected': 'attributes.connected'},
{'Idle': 'attributes.idle'},
{'Service': 'relationships.services.data[].id'}
])
}) })
}) })
.command('filters', 'List filters', function(yargs) { .command('filters', 'List filters', function(yargs) {
return yargs.epilog('List all filters in MaxScale.') return yargs.epilog('List all filters in MaxScale.' +
fieldDescriptions(list_filters_fields))
.usage('Usage: list filters') .usage('Usage: list filters')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'filters', [ return getCollection(host, 'filters', list_filters_fields)
{'Filter': 'id'},
{'Service': 'relationships.services.data[].id'},
{'Module': 'attributes.module'}
])
}) })
}) })
.command('modules', 'List loaded modules', function(yargs) { .command('modules', 'List loaded modules', function(yargs) {
return yargs.epilog('List all currently loaded modules.') return yargs.epilog('List all currently loaded modules.' +
fieldDescriptions(list_modules_fields))
.usage('Usage: list modules') .usage('Usage: list modules')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'maxscale/modules',[ return getCollection(host, 'maxscale/modules', list_modules_fields)
{'Module':'id'},
{'Type':'attributes.module_type'},
{'Version': 'attributes.version'}
])
}) })
}) })
.command('threads', 'List threads', function(yargs) { .command('threads', 'List threads', function(yargs) {
return yargs.epilog('List all worker threads.') return yargs.epilog('List all worker threads.' +
fieldDescriptions(list_threads_fields))
.usage('Usage: list threads') .usage('Usage: list threads')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'maxscale/threads', [ return getCollection(host, 'maxscale/threads', list_threads_fields)
{'Id': 'id'},
{'Current FDs': 'attributes.stats.current_descriptors'},
{'Total FDs': 'attributes.stats.total_descriptors'},
{'Load (1s)': 'attributes.stats.load.last_second'},
{'Load (1m)': 'attributes.stats.load.last_minute'},
{'Load (1h)': 'attributes.stats.load.last_hour'}
])
}) })
}) })
.command('users', 'List created users', function(yargs) { .command('users', 'List created users', function(yargs) {
return yargs.epilog('List network the users that can be used to connect to the MaxScale REST API as well as enabled local accounts.') return yargs.epilog('List network the users that can be used to connect to the MaxScale REST API' +
' as well as enabled local accounts.' +
fieldDescriptions(list_users_fields))
.usage('Usage: list users') .usage('Usage: list users')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'users',[ return getCollection(host, 'users', list_users_fields)
{'Name':'id'},
{'Type':'type'},
{'Privileges':'attributes.account'},
])
}) })
}) })
.command('commands', 'List module commands', function(yargs) { .command('commands', 'List module commands', function(yargs) {
return yargs.epilog('List all available module commands.') return yargs.epilog('List all available module commands.' +
fieldDescriptions(list_commands_fields))
.usage('Usage: list commands') .usage('Usage: list commands')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getCollection(host, 'maxscale/modules',[ return getCollection(host, 'maxscale/modules', list_commands_fields)
{'Module':'id'},
{'Commands': 'attributes.commands[].id'}
])
}) })
}) })
.usage('Usage: list <command>') .usage('Usage: list <command>')

View File

@ -14,94 +14,438 @@
require('./common.js')() require('./common.js')()
const server_fields = [ const server_fields = [
{'Server': 'id'}, {
{'Address': 'attributes.parameters.address'}, name: 'Server',
{'Port': 'attributes.parameters.port'}, path: 'id',
{'State': 'attributes.state'}, description: 'Server name'
{'Last Event': 'attributes.last_event'}, },
{'Triggered At': 'attributes.triggered_at'}, {
{'Services': 'relationships.services.data[].id'}, name: 'Address',
{'Monitors': 'relationships.monitors.data[].id'}, path: 'attributes.parameters.address',
{'Master ID': 'attributes.master_id'}, description: 'Address where the server listens'
{'Node ID': 'attributes.node_id'}, },
{'Slave Server IDs': 'attributes.slaves'}, {
{'Statistics': 'attributes.statistics'}, name: 'Port',
{'Parameters': 'attributes.parameters'} path: 'attributes.parameters.port',
description: 'The port on which the server listens'
},
{
name: 'State',
path: 'attributes.state',
description: 'Server state'
},
{
name: 'Last Event',
path: 'attributes.last_event',
description: 'The type of the latest event'
},
{
name: 'Triggered At',
path: 'attributes.triggered_at',
description: 'Time when the latest event was triggered at'
},
{
name: 'Services',
path: 'relationships.services.data[].id',
description: 'Services that use this server'
},
{
name: 'Monitors',
path: 'relationships.monitors.data[].id',
description: 'Monitors that monitor this server'
},
{
name: 'Master ID',
path: 'attributes.master_id',
description: 'The server ID of the master'
},
{
name: 'Node ID',
path: 'attributes.node_id',
description: 'The node ID of this server'
},
{
name: 'Slave Server IDs',
path: 'attributes.slaves',
description: 'List of slave server IDs'
},
{
name: 'Statistics',
path: 'attributes.statistics',
description: 'Server statistics'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'Server parameters'
}
] ]
const service_fields = [ const service_fields = [
{'Service': 'id'}, {
{'Router': 'attributes.router'}, name: 'Service',
{'State': 'attributes.state'}, path: 'id',
{'Started At': 'attributes.started'}, description: 'Service name'
{'Current Connections': 'attributes.connections'}, },
{'Total Connections': 'attributes.total_connections'}, {
{'Servers': 'relationships.servers.data[].id'}, name: 'Router',
{'Parameters': 'attributes.parameters'}, path: 'attributes.router',
{'Router Diagnostics': 'attributes.router_diagnostics'} description: 'Router that the service uses'
},
{
name: 'State',
path: 'attributes.state',
description: 'Service state'
},
{
name: 'Started At',
path: 'attributes.started',
description: 'When the service was started'
},
{
name: 'Current Connections',
path: 'attributes.connections',
description: 'Current connection count'
},
{
name: 'Total Connections',
path: 'attributes.total_connections',
description: 'Total connection count'
},
{
name: 'Servers',
path: 'relationships.servers.data[].id',
description: 'Servers that the service uses'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'Service parameter'
},
{
name: 'Router Diagnostics',
path: 'attributes.router_diagnostics',
description: 'Diagnostics provided by the router module'
}
] ]
const monitor_fields = [ const monitor_fields = [
{'Monitor': 'id'}, {
{'State': 'attributes.state'}, name: 'Monitor',
{'Servers': 'relationships.servers.data[].id'}, path: 'id',
{'Parameters': 'attributes.parameters'}, description: 'Monitor name'
{'Monitor Diagnostics': 'attributes.monitor_diagnostics'} },
{
name: 'State',
path: 'attributes.state',
description: 'Monitor state'
},
{
name: 'Servers',
path: 'relationships.servers.data[].id',
description: 'The servers that this monitor monitors'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'Monitor parameters'
},
{
name: 'Monitor Diagnostics',
path: 'attributes.monitor_diagnostics',
description: 'Diagnostics provided by the monitor module'
}
] ]
const session_fields = [ const session_fields = [
{'Id': 'id'}, {
{'Service': 'relationships.services.data[].id'}, name: 'Id',
{'State': 'attributes.state'}, path: 'id',
{'User': 'attributes.user'}, description: 'Session ID'
{'Host': 'attributes.remote'}, },
{'Connected': 'attributes.connected'}, {
{'Idle': 'attributes.idle'}, name: 'Service',
{'Connections': 'attributes.connections[].server'}, path: 'relationships.services.data[].id',
{'Connection IDs': 'attributes.connections[].protocol_diagnostics.connection_id'}, description: 'The service where the session connected'
{'Queries': 'attributes.queries[].statement'}, },
{'Log': 'attributes.log'} {
name: 'State',
path: 'attributes.state',
description: 'Session state'
},
{
name: 'User',
path: 'attributes.user',
description: 'Username'
},
{
name: 'Host',
path: 'attributes.remote',
description: 'Client host address'
},
{
name: 'Connected',
path: 'attributes.connected',
description: 'Time when the session started'
},
{
name: 'Idle',
path: 'attributes.idle',
description: 'How long the session has been idle, in seconds'
},
{
name: 'Connections',
path: 'attributes.connections[].server',
description: 'Ordered list of backend connections'
},
{
name: 'Connection IDs',
path: 'attributes.connections[].protocol_diagnostics.connection_id',
description: 'Thread IDs for the backend connections'
},
{
name: 'Queries',
path: 'attributes.queries[].statement',
description: 'Query history'
},
{
name: 'Log',
path: 'attributes.log',
description: 'Per-session log messages'
}
] ]
const filter_fields = [ const filter_fields = [
{'Filter': 'id'}, {
{'Module': 'attributes.module'}, name: 'Filter',
{'Services': 'relationships.services.data[].id'}, path: 'id',
{'Parameters': 'attributes.parameters'} description: 'Filter name'
},
{
name: 'Module',
path: 'attributes.module',
description: 'The module that the filter uses'
},
{
name: 'Services',
path: 'relationships.services.data[].id',
description: 'Services that use the filter'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'Filter parameters'
}
] ]
const module_fields = [ const module_fields = [
{'Module': 'id'}, {
{'Type': 'attributes.module_type'}, name: 'Module',
{'Version': 'attributes.version'}, path: 'id',
{'Maturity': 'attributes.maturity'}, description: 'Module name'
{'Description': 'attributes.description'}, },
{'Parameters': 'attributes.parameters'}, {
{'Commands': 'attributes.commands'} name: 'Type',
path: 'attributes.module_type',
description: 'Module type'
},
{
name: 'Version',
path: 'attributes.version',
description: 'Module version'
},
{
name: 'Maturity',
path: 'attributes.maturity',
description: 'Module maturity'
},
{
name: 'Description',
path: 'attributes.description',
description: 'Short description about the module'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'All the parameters that the module accepts'
},
{
name: 'Commands',
path: 'attributes.commands',
description: 'Commands that the module provides'
}
] ]
const thread_fields = [ const thread_fields = [
{'Id': 'id'}, {
{'Accepts': 'attributes.stats.accepts'}, name: 'Id',
{'Reads': 'attributes.stats.reads'}, path: 'id',
{'Writes': 'attributes.stats.writes'}, description: 'Thread ID'
{'Hangups': 'attributes.stats.hangups'}, },
{'Errors': 'attributes.stats.errors'}, {
{'Blocking polls': 'attributes.stats.blocking_polls'}, name: 'Accepts',
{'Avg event queue length': 'attributes.stats.avg_event_queue_length'}, path: 'attributes.stats.accepts',
{'Max event queue length': 'attributes.stats.max_event_queue_length'}, description: 'Number of TCP accepts done by this thread'
{'Max exec time': 'attributes.stats.max_exec_time'}, },
{'Max queue time': 'attributes.stats.max_queue_time'}, {
{'Current FDs': 'attributes.stats.current_descriptors'}, name: 'Reads',
{'Total FDs': 'attributes.stats.total_descriptors'}, path: 'attributes.stats.reads',
{'Load (1s)': 'attributes.stats.load.last_second'}, description: 'Number of EPOLLIN events'
{'Load (1m)': 'attributes.stats.load.last_minute'}, },
{'Load (1h)': 'attributes.stats.load.last_hour'}, {
{'QC cache size': 'attributes.stats.query_classifier_cache.size'}, name: 'Writes',
{'QC cache inserts': 'attributes.stats.query_classifier_cache.inserts'}, path: 'attributes.stats.writes',
{'QC cache hits': 'attributes.stats.query_classifier_cache.hits'}, description: 'Number of EPOLLOUT events'
{'QC cache misses': 'attributes.stats.query_classifier_cache.misses'}, },
{'QC cache evictions': 'attributes.stats.query_classifier_cache.evictions'}, {
name: 'Hangups',
path: 'attributes.stats.hangups',
description: 'Number of EPOLLHUP and EPOLLRDUP events'
},
{
name: 'Errors',
path: 'attributes.stats.errors',
description: 'Number of EPOLLERR events'
},
{
name: 'Avg event queue length',
path: 'attributes.stats.avg_event_queue_length',
description: 'Average number of events returned by one epoll_wait call'
},
{
name: 'Max event queue length',
path: 'attributes.stats.max_event_queue_length',
description: 'Maximum number of events returned by one epoll_wait call'
},
{
name: 'Max exec time',
path: 'attributes.stats.max_exec_time',
description: 'The longest time spent processing events returned by a epoll_wait call'
},
{
name: 'Max queue time',
path: 'attributes.stats.max_queue_time',
description: 'The longest time an event had to wait before it was processed'
},
{
name: 'Current FDs',
path: 'attributes.stats.current_descriptors',
description: 'Current number of managed file descriptors'
},
{
name: 'Total FDs',
path: 'attributes.stats.total_descriptors',
description: 'Total number of managed file descriptors'
},
{
name: 'Load (1s)',
path: 'attributes.stats.load.last_second',
description: 'Load percentage over the last second'
},
{
name: 'Load (1m)',
path: 'attributes.stats.load.last_minute',
description: 'Load percentage over the last minute'
},
{
name: 'Load (1h)',
path: 'attributes.stats.load.last_hour',
description: 'Load percentage over the last hour'
},
{
name: 'QC cache size',
path: 'attributes.stats.query_classifier_cache.size',
description: 'Query classifier size'
},
{
name: 'QC cache inserts',
path: 'attributes.stats.query_classifier_cache.inserts',
description: 'Number of times a new query was added into the query classification cache'
},
{
name: 'QC cache hits',
path: 'attributes.stats.query_classifier_cache.hits',
description: 'How many times a query classification was found in the query classification cache'
},
{
name: 'QC cache misses',
path: 'attributes.stats.query_classifier_cache.misses',
description: 'How many times a query classification was not found in the query classification cache'
},
{
name: 'QC cache evictions',
path: 'attributes.stats.query_classifier_cache.evictions',
description: 'How many times a query classification result was evicted from the query classification cache'
},
]
const show_maxscale_fields = [
{
name: 'Version',
path: 'attributes.version',
description: 'MaxScale version'
},
{
name: 'Commit',
path: 'attributes.commit',
description: 'MaxScale commit ID'
},
{
name: 'Started At',
path: 'attributes.started_at',
description: 'Time when MaxScale was started'
},
{
name: 'Activated At',
path: 'attributes.activated_at',
description: 'Time when MaxScale left passive mode'
},
{
name: 'Uptime',
path: 'attributes.uptime',
description: 'Time MaxScale has been running'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'Global MaxScale parameters'
}
]
const show_logging_fields = [
{
name: 'Current Log File',
path: 'attributes.log_file',
description: 'The current log file MaxScale is logging into'
},
{
name: 'Enabled Log Levels',
path: 'attributes.log_priorities',
description: 'List of log levels enabled in MaxScale'
},
{
name: 'Parameters',
path: 'attributes.parameters',
description: 'Logging parameters'
}
]
const show_commands_fields = [
{
name: 'Command',
path: 'id',
description: 'Command name'
},
{
name: 'Parameters',
path: 'attributes.parameters[].type',
description: 'Parameters the command supports'
},
{
name: 'Descriptions',
path: 'attributes.parameters[].description',
description: 'Parameter descriptions'
}
] ]
exports.command = 'show <command>' exports.command = 'show <command>'
@ -113,7 +457,7 @@ exports.builder = function(yargs) {
return yargs.epilog('Show detailed information about a server. The `Parameters` ' + return yargs.epilog('Show detailed information about a server. The `Parameters` ' +
'field contains the currently configured parameters for this ' + 'field contains the currently configured parameters for this ' +
'server. See `help alter server` for more details about altering ' + 'server. See `help alter server` for more details about altering ' +
'server parameters.') 'server parameters.' + fieldDescriptions(server_fields))
.usage('Usage: show server <server>') .usage('Usage: show server <server>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -121,7 +465,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('servers', 'Show all servers', function(yargs) { .command('servers', 'Show all servers', function(yargs) {
return yargs.epilog('Show detailed information about all servers.') return yargs.epilog('Show detailed information about all servers.' +
fieldDescriptions(server_fields))
.usage('Usage: show servers') .usage('Usage: show servers')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -132,7 +477,7 @@ exports.builder = function(yargs) {
return yargs.epilog('Show detailed information about a service. The `Parameters` ' + return yargs.epilog('Show detailed information about a service. The `Parameters` ' +
'field contains the currently configured parameters for this ' + 'field contains the currently configured parameters for this ' +
'service. See `help alter service` for more details about altering ' + 'service. See `help alter service` for more details about altering ' +
'service parameters.') 'service parameters.' + fieldDescriptions(service_fields))
.usage('Usage: show service <service>') .usage('Usage: show service <service>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -140,7 +485,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('services', 'Show all services', function(yargs) { .command('services', 'Show all services', function(yargs) {
return yargs.epilog('Show detailed information about all services.') return yargs.epilog('Show detailed information about all services.' +
fieldDescriptions(service_fields))
.usage('Usage: show services') .usage('Usage: show services')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -151,7 +497,7 @@ exports.builder = function(yargs) {
return yargs.epilog('Show detailed information about a monitor. The `Parameters` ' + return yargs.epilog('Show detailed information about a monitor. The `Parameters` ' +
'field contains the currently configured parameters for this ' + 'field contains the currently configured parameters for this ' +
'monitor. See `help alter monitor` for more details about altering ' + 'monitor. See `help alter monitor` for more details about altering ' +
'monitor parameters.') 'monitor parameters.' + fieldDescriptions(monitor_fields))
.usage('Usage: show monitor <monitor>') .usage('Usage: show monitor <monitor>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -159,7 +505,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('monitors', 'Show all monitors', function(yargs) { .command('monitors', 'Show all monitors', function(yargs) {
return yargs.epilog('Show detailed information about all monitors.') return yargs.epilog('Show detailed information about all monitors.' +
fieldDescriptions(monitor_fields))
.usage('Usage: show monitors') .usage('Usage: show monitors')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -173,7 +520,8 @@ exports.builder = function(yargs) {
'ID of a particular session.\n\n' + 'ID of a particular session.\n\n' +
'The `Connections` field lists the servers to which ' + 'The `Connections` field lists the servers to which ' +
'the session is connected and the `Connection IDs` ' + 'the session is connected and the `Connection IDs` ' +
'field lists the IDs for those connections.') 'field lists the IDs for those connections.' +
fieldDescriptions(session_fields))
.usage('Usage: show session <session>') .usage('Usage: show session <session>')
.group([rDnsOption.shortname], 'Options:') .group([rDnsOption.shortname], 'Options:')
.option(rDnsOption.shortname, rDnsOption.definition) .option(rDnsOption.shortname, rDnsOption.definition)
@ -188,7 +536,8 @@ exports.builder = function(yargs) {
}) })
.command('sessions', 'Show all sessions', function(yargs) { .command('sessions', 'Show all sessions', function(yargs) {
return yargs.epilog('Show detailed information about all sessions. ' + return yargs.epilog('Show detailed information about all sessions. ' +
'See `help show session` for more details.') 'See `help show session` for more details.' +
fieldDescriptions(session_fields))
.usage('Usage: show sessions') .usage('Usage: show sessions')
.group([rDnsOption.shortname], 'Options:') .group([rDnsOption.shortname], 'Options:')
.option(rDnsOption.shortname, rDnsOption.definition) .option(rDnsOption.shortname, rDnsOption.definition)
@ -202,7 +551,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('filter <filter>', 'Show filter', function(yargs) { .command('filter <filter>', 'Show filter', function(yargs) {
return yargs.epilog('The list of services that use this filter is show in the `Services` field.') return yargs.epilog('The list of services that use this filter is show in the `Services` field.' +
fieldDescriptions(filter_fields))
.usage('Usage: show filter <filter>') .usage('Usage: show filter <filter>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -210,7 +560,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('filters', 'Show all filters', function(yargs) { .command('filters', 'Show all filters', function(yargs) {
return yargs.epilog('Show detailed information of all filters.') return yargs.epilog('Show detailed information of all filters.' +
fieldDescriptions(filter_fields))
.usage('Usage: show filters') .usage('Usage: show filters')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -219,7 +570,8 @@ exports.builder = function(yargs) {
}) })
.command('module <module>', 'Show loaded module', function(yargs) { .command('module <module>', 'Show loaded module', function(yargs) {
return yargs.epilog('This command shows all available parameters as well as ' + return yargs.epilog('This command shows all available parameters as well as ' +
'detailed version information of a loaded module.') 'detailed version information of a loaded module.' +
fieldDescriptions(module_fields))
.usage('Usage: show module <module>') .usage('Usage: show module <module>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -227,7 +579,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('modules', 'Show all loaded modules', function(yargs) { .command('modules', 'Show all loaded modules', function(yargs) {
return yargs.epilog('Displays detailed information about all modules.') return yargs.epilog('Displays detailed information about all modules.' +
fieldDescriptions(module_fields))
.usage('Usage: show modules') .usage('Usage: show modules')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -236,22 +589,16 @@ exports.builder = function(yargs) {
}) })
.command('maxscale', 'Show MaxScale information', function(yargs) { .command('maxscale', 'Show MaxScale information', function(yargs) {
return yargs.epilog('See `help alter maxscale` for more details about altering ' + return yargs.epilog('See `help alter maxscale` for more details about altering ' +
'MaxScale parameters.') 'MaxScale parameters.' + fieldDescriptions(show_maxscale_fields))
.usage('Usage: show maxscale') .usage('Usage: show maxscale')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getResource(host, 'maxscale', [ return getResource(host, 'maxscale', show_maxscale_fields)
{'Version': 'attributes.version'},
{'Commit': 'attributes.commit'},
{'Started At': 'attributes.started_at'},
{'Activated At': 'attributes.activated_at'},
{'Uptime': 'attributes.uptime'},
{'Parameters': 'attributes.parameters'}
])
}) })
}) })
.command('thread <thread>', 'Show thread', function(yargs) { .command('thread <thread>', 'Show thread', function(yargs) {
return yargs.epilog('Show detailed information about a worker thread.') return yargs.epilog('Show detailed information about a worker thread.' +
fieldDescriptions(thread_fields))
.usage('Usage: show thread <thread>') .usage('Usage: show thread <thread>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -259,7 +606,8 @@ exports.builder = function(yargs) {
}) })
}) })
.command('threads', 'Show all threads', function(yargs) { .command('threads', 'Show all threads', function(yargs) {
return yargs.epilog('Show detailed information about all worker threads.') return yargs.epilog('Show detailed information about all worker threads.' +
fieldDescriptions(thread_fields))
.usage('Usage: show threads') .usage('Usage: show threads')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
@ -268,28 +616,21 @@ exports.builder = function(yargs) {
}) })
.command('logging', 'Show MaxScale logging information', function(yargs) { .command('logging', 'Show MaxScale logging information', function(yargs) {
return yargs.epilog('See `help alter logging` for more details about altering ' + return yargs.epilog('See `help alter logging` for more details about altering ' +
'logging parameters.') 'logging parameters.' + fieldDescriptions(show_logging_fields))
.usage('Usage: show logging') .usage('Usage: show logging')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getResource(host, 'maxscale/logs', [ return getResource(host, 'maxscale/logs', show_logging_fields)
{'Current Log File': 'attributes.log_file'},
{'Enabled Log Levels': 'attributes.log_priorities'},
{'Parameters': 'attributes.parameters'}
])
}) })
}) })
.command('commands <module>', 'Show module commands of a module', function(yargs) { .command('commands <module>', 'Show module commands of a module', function(yargs) {
return yargs.epilog('This command shows the parameters the command expects with ' + return yargs.epilog('This command shows the parameters the command expects with ' +
'the parameter descriptions.') 'the parameter descriptions.' + fieldDescriptions(show_commands_fields))
.usage('Usage: show commands <module>') .usage('Usage: show commands <module>')
}, function(argv) { }, function(argv) {
maxctrl(argv, function(host) { maxctrl(argv, function(host) {
return getSubCollection(host, 'maxscale/modules/' + argv.module, 'attributes.commands', [ return getSubCollection(host, 'maxscale/modules/' + argv.module, 'attributes.commands',
{'Command': 'id'}, show_commands_fields)
{'Parameters': 'attributes.parameters[].type'},
{'Descriptions': 'attributes.parameters[].description'}
])
}) })
}) })
.command('qc_cache', 'Show query classifier cache', function(yargs) { .command('qc_cache', 'Show query classifier cache', function(yargs) {

View File

@ -1259,7 +1259,7 @@ mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char*
return rval; return rval;
} }
static inline bool is_next(mxs::Buffer::iterator it, mxs::Buffer::iterator end, const std::string& str) static inline bool is_next(uint8_t* it, uint8_t* end, const std::string& str)
{ {
mxb_assert(it != end); mxb_assert(it != end);
for (auto s_it = str.begin(); s_it != str.end(); ++s_it, ++it) for (auto s_it = str.begin(); s_it != str.end(); ++s_it, ++it)
@ -1316,12 +1316,11 @@ static const LUT is_special([](uint8_t c) {
c) != std::string::npos; c) != std::string::npos;
}); });
static std::pair<bool, mxs::Buffer::iterator> probe_number(mxs::Buffer::iterator it, static inline std::pair<bool, uint8_t*> probe_number(uint8_t* it, uint8_t* end)
mxs::Buffer::iterator end)
{ {
mxb_assert(it != end); mxb_assert(it != end);
mxb_assert(is_digit(*it)); mxb_assert(is_digit(*it));
std::pair<bool, mxs::Buffer::iterator> rval = std::make_pair(true, it); std::pair<bool, uint8_t*> rval = std::make_pair(true, it);
bool is_hex = *it == '0'; bool is_hex = *it == '0';
bool allow_hex = false; bool allow_hex = false;
@ -1348,7 +1347,7 @@ static std::pair<bool, mxs::Buffer::iterator> probe_number(mxs::Buffer::iterator
else if (*it == 'e') else if (*it == 'e')
{ {
// Possible scientific notation number // Possible scientific notation number
auto next_it = std::next(it); auto next_it = it + 1;
if (next_it == end || (!is_digit(*next_it) && *next_it != '-')) if (next_it == end || (!is_digit(*next_it) && *next_it != '-'))
{ {
@ -1365,7 +1364,7 @@ static std::pair<bool, mxs::Buffer::iterator> probe_number(mxs::Buffer::iterator
else if (*it == '.') else if (*it == '.')
{ {
// Possible decimal number // Possible decimal number
auto next_it = std::next(it); auto next_it = it + 1;
if (next_it != end && !is_digit(*next_it)) if (next_it != end && !is_digit(*next_it))
{ {
@ -1415,7 +1414,7 @@ static inline bool is_negation(const std::string& str, int i)
return rval; return rval;
} }
mxs::Buffer::iterator find_char(mxs::Buffer::iterator it, const mxs::Buffer::iterator& end, char c) static inline uint8_t* find_char(uint8_t* it, uint8_t* end, char c)
{ {
for (; it != end; ++it) for (; it != end; ++it)
{ {
@ -1440,13 +1439,13 @@ namespace maxscale
std::string get_canonical(GWBUF* querybuf) std::string get_canonical(GWBUF* querybuf)
{ {
std::string rval; mxb_assert(GWBUF_IS_CONTIGUOUS(querybuf));
uint8_t* it = GWBUF_DATA(querybuf) + MYSQL_HEADER_LEN + 1;
uint8_t* end = GWBUF_DATA(querybuf) + gwbuf_length(querybuf);
std::string rval(end - it, 0);
int i = 0; int i = 0;
rval.resize(gwbuf_length(querybuf) - MYSQL_HEADER_LEN + 1);
mxs::Buffer buf(querybuf);
for (auto it = std::next(buf.begin(), MYSQL_HEADER_LEN + 1); // Skip packet header and command for (; it != end; ++it)
it != buf.end(); ++it)
{ {
if (!is_special(*it)) if (!is_special(*it))
{ {
@ -1456,9 +1455,9 @@ std::string get_canonical(GWBUF* querybuf)
else if (*it == '\\') else if (*it == '\\')
{ {
// Jump over any escaped values // Jump over any escaped values
rval[i++] += *it++; rval[i++] = *it++;
if (it != buf.end()) if (it != end)
{ {
rval[i++] = *it; rval[i++] = *it;
} }
@ -1479,19 +1478,19 @@ std::string get_canonical(GWBUF* querybuf)
rval[i++] = ' '; rval[i++] = ' ';
} }
} }
else if (*it == '/' && is_next(it, buf.end(), "/*")) else if (*it == '/' && is_next(it, end, "/*"))
{ {
auto comment_start = std::next(it, 2); auto comment_start = std::next(it, 2);
if (comment_start == buf.end()) if (comment_start == end)
{ {
break; break;
} }
else if (*comment_start != '!' && *comment_start != 'M') else if (*comment_start != '!' && *comment_start != 'M')
{ {
// Non-executable comment // Non-executable comment
while (it != buf.end()) while (it != end)
{ {
if (is_next(it, buf.end(), "*/")) if (is_next(it, end, "*/"))
{ {
// Comment end marker, return to normal parsing // Comment end marker, return to normal parsing
++it; ++it;
@ -1500,7 +1499,7 @@ std::string get_canonical(GWBUF* querybuf)
++it; ++it;
} }
if (it == buf.end()) if (it == end)
{ {
break; break;
} }
@ -1512,10 +1511,10 @@ std::string get_canonical(GWBUF* querybuf)
} }
} }
else if ((*it == '#' || *it == '-') else if ((*it == '#' || *it == '-')
&& (is_next(it, buf.end(), "# ") || is_next(it, buf.end(), "-- "))) && (is_next(it, end, "# ") || is_next(it, end, "-- ")))
{ {
// End-of-line comment, jump to the next line if one exists // End-of-line comment, jump to the next line if one exists
while (it != buf.end()) while (it != end)
{ {
if (*it == '\n') if (*it == '\n')
{ {
@ -1523,7 +1522,7 @@ std::string get_canonical(GWBUF* querybuf)
} }
else if (*it == '\r') else if (*it == '\r')
{ {
if ((is_next(it, buf.end(), "\r\n"))) if ((is_next(it, end, "\r\n")))
{ {
++it; ++it;
} }
@ -1533,14 +1532,14 @@ std::string get_canonical(GWBUF* querybuf)
++it; ++it;
} }
if (it == buf.end()) if (it == end)
{ {
break; break;
} }
} }
else if (is_digit(*it) && (i == 0 || (!is_alnum(rval[i - 1]) && rval[i - 1] != '_'))) else if (is_digit(*it) && (i == 0 || (!is_alnum(rval[i - 1]) && rval[i - 1] != '_')))
{ {
auto num_end = probe_number(it, buf.end()); auto num_end = probe_number(it, end);
if (num_end.first) if (num_end.first)
{ {
@ -1556,7 +1555,7 @@ std::string get_canonical(GWBUF* querybuf)
else if (*it == '\'' || *it == '"') else if (*it == '\'' || *it == '"')
{ {
char c = *it; char c = *it;
if ((it = find_char(std::next(it), buf.end(), c)) == buf.end()) if ((it = find_char(it + 1, end, c)) == end)
{ {
break; break;
} }
@ -1565,7 +1564,7 @@ std::string get_canonical(GWBUF* querybuf)
else if (*it == '`') else if (*it == '`')
{ {
auto start = it; auto start = it;
if ((it = find_char(std::next(it), buf.end(), '`')) == buf.end()) if ((it = find_char(it + 1, end, '`')) == end)
{ {
break; break;
} }
@ -1578,7 +1577,7 @@ std::string get_canonical(GWBUF* querybuf)
rval[i++] = *it; rval[i++] = *it;
} }
mxb_assert(it != buf.end()); mxb_assert(it != end);
} }
// Remove trailing whitespace // Remove trailing whitespace
@ -1590,8 +1589,6 @@ std::string get_canonical(GWBUF* querybuf)
// Shrink the buffer so that the internal bookkeeping of std::string remains up to date // Shrink the buffer so that the internal bookkeeping of std::string remains up to date
rval.resize(i); rval.resize(i);
buf.release();
return rval; return rval;
} }
} }

View File

@ -208,6 +208,24 @@ void ResultSet::write(DCB* dcb)
mysql_send_eof(dcb, seqno); mysql_send_eof(dcb, seqno);
} }
json_t* ResultSet::get_json_value(const std::string& s)
{
json_t* js;
char* end;
long l = strtol(s.c_str(), &end, 10);
if (end != s.c_str() && *end == '\0')
{
js = json_integer(l);
}
else
{
js = json_string(s.c_str());
}
return js;
}
void ResultSet::write_as_json(DCB* dcb) void ResultSet::write_as_json(DCB* dcb)
{ {
json_t* arr = json_array(); json_t* arr = json_array();
@ -218,7 +236,7 @@ void ResultSet::write_as_json(DCB* dcb)
for (size_t i = 0; i < row.size(); i++) for (size_t i = 0; i < row.size(); i++)
{ {
json_object_set_new(obj, m_columns[i].c_str(), json_string(row[i].c_str())); json_object_set_new(obj, m_columns[i].c_str(), get_json_value(row[i]));
} }
json_array_append_new(arr, obj); json_array_append_new(arr, obj);
@ -227,4 +245,6 @@ void ResultSet::write_as_json(DCB* dcb)
char* js = json_dumps(arr, JSON_INDENT(4)); char* js = json_dumps(arr, JSON_INDENT(4));
dcb_printf(dcb, "%s", js); dcb_printf(dcb, "%s", js);
MXS_FREE(js); MXS_FREE(js);
json_decref(arr);
} }

View File

@ -62,7 +62,7 @@ struct
uint64_t next_session_id; uint64_t next_session_id;
uint32_t retain_last_statements; uint32_t retain_last_statements;
session_dump_statements_t dump_statements; session_dump_statements_t dump_statements;
uint32_t session_trace; uint32_t session_trace;
} this_unit = } this_unit =
{ {
1, 1,
@ -160,7 +160,6 @@ bool session_start(MXS_SESSION* session)
session->state = SESSION_STATE_STARTED; session->state = SESSION_STATE_STARTED;
mxb::atomic::add(&session->service->stats.n_sessions, 1, mxb::atomic::RELAXED); mxb::atomic::add(&session->service->stats.n_sessions, 1, mxb::atomic::RELAXED);
mxb::atomic::add(&session->service->stats.n_current, 1, mxb::atomic::RELAXED);
MXS_INFO("Started %s client session [%" PRIu64 "] for '%s' from %s", MXS_INFO("Started %s client session [%" PRIu64 "] for '%s' from %s",
session->service->name(), session->ses_id, session->service->name(), session->ses_id,
@ -279,8 +278,6 @@ static void session_final_free(MXS_SESSION* ses)
session->state = SESSION_STATE_TO_BE_FREED; session->state = SESSION_STATE_TO_BE_FREED;
mxb::atomic::add(&session->service->stats.n_current, -1, mxb::atomic::RELAXED);
if (session->client_dcb) if (session->client_dcb)
{ {
dcb_free_all_memory(session->client_dcb); dcb_free_all_memory(session->client_dcb);
@ -1162,6 +1159,9 @@ Session::Session(const SListener& listener)
{ {
m_retain_last_statements = this_unit.retain_last_statements; m_retain_last_statements = this_unit.retain_last_statements;
} }
mxb::atomic::add(&service->stats.n_current, 1, mxb::atomic::RELAXED);
mxb_assert(service->stats.n_current >= 0);
} }
Session::~Session() Session::~Session()
@ -1176,6 +1176,9 @@ Session::~Session()
f.filter->obj->closeSession(f.instance, f.session); f.filter->obj->closeSession(f.instance, f.session);
f.filter->obj->freeSession(f.instance, f.session); f.filter->obj->freeSession(f.instance, f.session);
} }
mxb::atomic::add(&service->stats.n_current, -1, mxb::atomic::RELAXED);
mxb_assert(service->stats.n_current >= 0);
} }
void Session::set_client_dcb(DCB* dcb) void Session::set_client_dcb(DCB* dcb)
@ -1696,6 +1699,6 @@ void Session::dump_session_log()
log += s; log += s;
} }
MXS_NOTICE("Session log for session (%" PRIu64"): \n%s ", ses_id, log.c_str()); MXS_NOTICE("Session log for session (%" PRIu64 "): \n%s ", ses_id, log.c_str());
} }
} }

View File

@ -1,4 +1,5 @@
add_executable(profile_trxboundaryparser profile_trxboundaryparser.cc) add_executable(profile_trxboundaryparser profile_trxboundaryparser.cc)
add_executable(profile_get_canonical profile_get_canonical.cc)
add_executable(test_adminusers test_adminusers.cc) add_executable(test_adminusers test_adminusers.cc)
add_executable(test_atomic test_atomic.cc) add_executable(test_atomic test_atomic.cc)
add_executable(test_buffer test_buffer.cc) add_executable(test_buffer test_buffer.cc)
@ -28,6 +29,7 @@ add_executable(test_utils test_utils.cc)
add_executable(test_session_track test_session_track.cc) add_executable(test_session_track test_session_track.cc)
target_link_libraries(profile_trxboundaryparser maxscale-common) target_link_libraries(profile_trxboundaryparser maxscale-common)
target_link_libraries(profile_get_canonical maxscale-common)
target_link_libraries(test_adminusers maxscale-common) target_link_libraries(test_adminusers maxscale-common)
target_link_libraries(test_atomic maxscale-common) target_link_libraries(test_atomic maxscale-common)
target_link_libraries(test_buffer maxscale-common) target_link_libraries(test_buffer maxscale-common)

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2022-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/ccdefs.hh>
#include <maxscale/modutil.hh>
#include <iostream>
#include <sstream>
#include <chrono>
using Clock = std::chrono::steady_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
int main(int argc, char* argv[])
{
int ITERATIONS = 10000000;
for (std::string line; std::getline(std::cin, line);)
{
GWBUF* buf = modutil_create_query(line.c_str());
auto start = Clock::now();
for (int i = 0; i < ITERATIONS; i++)
{
auto str = mxs::get_canonical(buf);
}
auto end = Clock::now();
gwbuf_free(buf);
std::cout << line << "\n"
<< duration_cast<milliseconds>(end - start).count() << "ms\n\n";
}
return 0;
}

View File

@ -26,8 +26,6 @@ Session::Session(Client* pClient, const SListener& listener)
{ {
MXS_SESSION* pSession = this; MXS_SESSION* pSession = this;
memset((void*)pSession, 0, sizeof(MXS_SESSION));
pSession->state = SESSION_STATE_CREATED; pSession->state = SESSION_STATE_CREATED;
pSession->client_dcb = &m_client_dcb; pSession->client_dcb = &m_client_dcb;

View File

@ -1419,13 +1419,15 @@ static void worker_func(int thread_id, void* data)
for (TargetList::iterator it = info->targets.begin(); for (TargetList::iterator it = info->targets.begin();
it != info->targets.end(); it++) it != info->targets.end(); it++)
{ {
LocalClient* client = LocalClient::create(&info->session, &info->protocol, it->first); if (LocalClient* client = LocalClient::create(&info->session, &info->protocol, it->first))
GWBUF* buffer = modutil_create_query(it->second.c_str()); {
client->queue_query(buffer); GWBUF* buffer = modutil_create_query(it->second.c_str());
gwbuf_free(buffer); client->queue_query(buffer);
gwbuf_free(buffer);
// The LocalClient needs to delete itself once the queries are done // The LocalClient needs to delete itself once the queries are done
client->self_destruct(); client->self_destruct();
}
} }
delete info; delete info;