Skip to content

Instantly share code, notes, and snippets.

@thisnameissoclever
Last active October 4, 2025 17:29
Show Gist options
  • Save thisnameissoclever/91064abea75a8dcfdf6669c194a4d888 to your computer and use it in GitHub Desktop.
Save thisnameissoclever/91064abea75a8dcfdf6669c194a4d888 to your computer and use it in GitHub Desktop.
This script scans all scripts in a given table/field for GlideRecord queries that use `if (gr.next())` WITHOUT a preceding '.setLimit(1)' before '.query()'.
/*
Single-Record GlideRecord Query Violation Detector (with function & line info)
This script scans all scripts in a given table/field for GlideRecord queries that use
`if (gr.next())` WITHOUT a preceding '.setLimit(1)' before '.query()'.
Documentation: https://filthyqueries.snc.guru
Latest version URL: https://filthyqueries-gist.snc.guru
Author: Tim Woodruff (snprotips.com)
Version: 1.05
License: Open Source (MIT License)
Date (US): 7/23/2025
*/
//Set arg to `true` to print detailed violations with links and line numbers.
debugPrintCommonViolations(true);
/*
EXAMPLE OUTPUT without details:
Found 5 tables with violations:
* sys_script_include: 840 violations
* sys_script: 508 violations
* sys_ws_operation: 22 violations
* sys_trigger: 1 violations
* sysevent_script_action: 20 violations
Total violations: 1391
*/
function debugPrintCommonViolations(printDetails) {
var tableName;
var logMsg = '';
var totalViolationCount = 0;
var objViolations = {
'sys_script_include' : findDirtyFilthyUglyBadQueries('sys_script_include', 'script'),
'sys_script' : findDirtyFilthyUglyBadQueries('sys_script', 'script'),
'sys_ws_operation' : findDirtyFilthyUglyBadQueries('sys_ws_operation', 'operation_script'),
'sys_trigger' : findDirtyFilthyUglyBadQueries('sys_trigger', 'script'),
'sysevent_script_action' : findDirtyFilthyUglyBadQueries('sysevent_script_action', 'script'),
'sys_variable_value': findDirtyFilthyUglyBadQueries('sys_variable_value', 'value', 'variable.labelLIKEscript')
};
logMsg += 'Found ' + Object.keys(objViolations).length + ' tables with violations:\n';
for (tableName in objViolations) {
if (!objViolations.hasOwnProperty(tableName)) {
continue; // Skip non-own properties
}
totalViolationCount += objViolations[tableName].violation_count;
logMsg += '* ' + tableName + ': ' + objViolations[tableName].violation_count + ' violations\n';
}
logMsg += '\nTotal violations: ' + totalViolationCount + '\n';
gs.debug(logMsg);
if (printDetails) {
gs.debug(
'Detailed violations: \n' +
JSON.stringify(
objViolations, null, 2
)
);
}
}
/**
* Finds all scripts in the specified table and field that contain single-record queries
* that do not use `.setLimit(1)` before `.query()`.
*
* @param {String} tableName - The name of the table to search for violations in scripts.
*
* @param {String} fieldName - The name of the field in the table that contains the script to
* analyze.
*
* @param {String} [filter] - Optional. An encoded query to apply when querying the table.
*
* @returns {{table: string, field: string, checkedCount: number, violation_count: number,
* violations: *[]}}
*/
function findDirtyFilthyUglyBadQueries(tableName, fieldName, filter) {
//Main function: Find all Script Includes with single-record query violations
//init: set default values for tableName and fieldName
tableName = tableName || 'sys_script_include';
fieldName = fieldName || 'script';
//init: hoist and initialize variables
var scriptBody, recordName, recordSysId;
var grScript = new GlideRecord(tableName);
var result = {
table : tableName,
field : fieldName,
checkedCount : 0,
violation_count : 0,
violations : []
};
grScript.addActiveQuery(); // Only check active records, if active field exists.
if (filter) {
grScript.addEncodedQuery(filter);
}
grScript.query();
while (grScript.next()) {
result.checkedCount++;
scriptBody = grScript.getValue(fieldName);
if (!scriptBody) {
continue;
}
recordName = grScript.getValue('name') || grScript.getDisplayValue();
recordSysId = grScript.getValue('sys_id');
analyzeScript(
result.violations,
scriptBody,
recordName,
recordSysId
);
}
// Populate the results object before returning
result.violation_count = result.violations.length;
/*gs.info('Checked ' + checked + ' records in ' + tableName + '. Found ' + violations.length + ' single-record query violations (missing .setLimit(1)):');
gs.info(JSON.stringify(violations, null, 2));*/
return result;
// ================== Helpers are below ===================
function analyzeScript(arrViolations, scriptBody, recordName, recordSysId) {
var iLine, iRecord, iRecordForIf, ifNextMatch;
var line, matchNew, functionName, methodMatch, matchingGR, matchingGRForIf;
var lines = scriptBody.split('\n');
var glideRecords = [];
var regexNewGR = /var\s+([a-zA-Z0-9_]+)\s*=\s*new\s+GlideRecord\s*\(\s*['"][a-zA-Z0-9_]+['"]\s*\)/;
var regexAssignGR = /([a-zA-Z0-9_]+)\s*=\s*new\s+GlideRecord\s*\(\s*['"][a-zA-Z0-9_]+['"]\s*\)/;
var regexMethodCall = /([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)\s*\(/;
var regexIfNext = /if\s*\(\s*([a-zA-Z0-9_]+)\.next\s*\(\s*\)\s*\)/;
for (iLine = 0; iLine < lines.length; iLine++) {
line = lines[iLine];
matchNew = regexNewGR.exec(line) || regexAssignGR.exec(line);
if (matchNew) {
functionName = getFunctionNameForLine(lines, iLine);
glideRecords.push({
varName : matchNew[1],
instLine : iLine,
methods : [],
queryLine : null,
setLimitLine : null,
ifNextLines : [],
functionName : functionName
});
}
methodMatch = regexMethodCall.exec(line);
if (methodMatch) {
matchingGR = null;
for (iRecord = glideRecords.length - 1; iRecord >= 0; iRecord--) {
if (glideRecords[iRecord].varName === methodMatch[1] && glideRecords[iRecord].instLine <= iLine) {
matchingGR = glideRecords[iRecord];
break;
}
}
if (matchingGR) {
matchingGR.methods.push({name : methodMatch[2], line : iLine});
if (methodMatch[2] === 'query') {
matchingGR.queryLine = iLine;
} else if (methodMatch[2] === 'setLimit') {
matchingGR.setLimitLine = iLine;
}
}
}
ifNextMatch = regexIfNext.exec(line);
if (ifNextMatch) {
matchingGRForIf = null;
for (iRecordForIf = glideRecords.length - 1; iRecordForIf >= 0; iRecordForIf--) {
if (glideRecords[iRecordForIf].varName === ifNextMatch[1] && glideRecords[iRecordForIf].instLine <= iLine) {
matchingGRForIf = glideRecords[iRecordForIf];
break;
}
}
if (matchingGRForIf) {
matchingGRForIf.ifNextLines.push(iLine);
}
}
}
glideRecords.forEach(function(objRecord) {
objRecord.ifNextLines.forEach(function(ifLine) {
var hasSetLimitBeforeQuery;
if (
objRecord.queryLine !== null &&
objRecord.queryLine < ifLine
) {
hasSetLimitBeforeQuery = (
objRecord.setLimitLine !== null &&
objRecord.setLimitLine < objRecord.queryLine
);
if (!hasSetLimitBeforeQuery) {
arrViolations.push({
name : recordName,
sys_id : recordSysId,
url : buildRecordURL(recordSysId),
variable : objRecord.varName,
functionName : objRecord.functionName,
instLine : objRecord.instLine + 1 // 1-based line number
});
}
}
});
});
}
function getFunctionNameForLine(lines, targetLine) {
var iTargetLine, line, funcMatch, objFuncMatch, functionName;
for (iTargetLine = targetLine; iTargetLine >= 0; iTargetLine--) {
line = lines[iTargetLine];
funcMatch = /function\s+([a-zA-Z0-9_]+)\s*\(/.exec(line);
if (funcMatch) {
functionName = funcMatch[1];
break;
}
objFuncMatch = /([a-zA-Z0-9_]+)\s*:\s*function\s*\(/.exec(line);
if (objFuncMatch) {
functionName = objFuncMatch[1];
break;
}
}
return functionName || 'global or anonymous';
}
function buildRecordURL(sysId) {
return (
gs.getProperty('glide.servlet.uri') + tableName +
'.do?sys_id=' + sysId
);
}
}

NOTE: This script can print out the details of where each violation can be found, including a link to the specific script file, line number, and even each variable name with an inefficient single-record query violation. To print these details, set the boolean argument on line 14 to true. To print only a summary of findings per table, set that boolean to false.


Found 6 tables with violations:

  • sys_script_include: 840 violations
  • sys_script: 508 violations
  • sys_ws_operation: 22 violations
  • sys_trigger: 1 violations
  • sysevent_script_action: 20 violations
  • sys_variable_value: 43 violations

Total violations: 1434

NOTE: This script can print out the details of where each violation can be found, including a link to the specific script file, line number, and even each variable name with an inefficient single-record query violation. To print these details, set the boolean argument on line 14 to true. To print only a summary of findings per table, set that boolean to false.


Detailed violations:

{
	"sys_script_include": {
		"table": "sys_script_include",
		"field": "script",
		"checkedCount": 4846,
		"violation_count": 840,
		"violations": [
			{
				"name": "ScheduledInstallService",
				"sys_id": "000b82eb93312010ebd4f157b67ffb86",
				"url": "https://dev271144.service-now.com/sys_script_include.do?sys_id=000b82eb93312010ebd4f157b67ffb86",
				"variable": "gr",
				"functionName": "deleteScheduleItem",
				"instLine": 239
			},
			{
				"name": "ScheduledInstallService",
				"sys_id": "000b82eb93312010ebd4f157b67ffb86",
				"url": "https://dev271144.service-now.com/sys_script_include.do?sys_id=000b82eb93312010ebd4f157b67ffb86",
				"variable": "scheduleGr",
				"functionName": "fetchSchedule",
				"instLine": 254
			},
			{
				"name": "ScheduledInstallService",
				"sys_id": "000b82eb93312010ebd4f157b67ffb86",
				"url": "https://dev271144.service-now.com/sys_script_include.do?sys_id=000b82eb93312010ebd4f157b67ffb86",
				"variable": "gr",
				"functionName": "updateScheduledInstallItem",
				"instLine": 640
			},
            ...
		]
	},
    ...
	"sys_variable_value": {
		"table": "sys_variable_value",
		"field": "value",
		"checkedCount": 2017,
		"violation_count": 43,
		"violations": [
			{
				"name": "Created 2021-01-04 12:22:17",
				"sys_id": "223c08c17321201056dff358caf6a7d6",
				"url": "https://dev271144.service-now.com/sys_variable_value.do?sys_id=223c08c17321201056dff358caf6a7d6",
				"variable": "searchProfileGr",
				"functionName": "getSearchProfileName",
				"instLine": 22
			},
			{
				"name": "Created 2024-09-05 13:14:51",
				"sys_id": "2aecf09053241210aec2ddeeff7b12fa",
				"url": "https://dev271144.service-now.com/sys_variable_value.do?sys_id=2aecf09053241210aec2ddeeff7b12fa",
				"variable": "capabilityDefinitionGr",
				"functionName": "execute",
				"instLine": 15
			},
			{
				"name": "Created 2024-09-05 13:14:51",
				"sys_id": "2aecf09053241210aec2ddeeff7b12fa",
				"url": "https://dev271144.service-now.com/sys_variable_value.do?sys_id=2aecf09053241210aec2ddeeff7b12fa",
				"variable": "scriptIncludeGr",
				"functionName": "execute",
				"instLine": 27
			},
			{
				"name": "Created 2024-05-27 06:03:04",
				"sys_id": "3081972f9f9202107c0e2305fa0a1ccd",
				"url": "https://dev271144.service-now.com/sys_variable_value.do?sys_id=3081972f9f9202107c0e2305fa0a1ccd",
				"variable": "scriptGr",
				"functionName": "processAttributes",
				"instLine": 65
			},
			...
		]
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment