Skip to content

Instantly share code, notes, and snippets.

@PhilippSalvisberg
Last active October 9, 2022 06:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PhilippSalvisberg/ce8d07c984daf32552e217c24a13c1c6 to your computer and use it in GitHub Desktop.
Save PhilippSalvisberg/ce8d07c984daf32552e217c24a13c1c6 to your computer and use it in GitHub Desktop.
Removing Quotes from Identifiers #JoelKallmanDay - see https://www.salvis.com/blog/2022/10/11/quoted-identifiers-joelkallmanday/
-- --------------------------------------------------------------------------------------------------------------------
-- Minimal Arbori program (expected by the formatter, also expected: "order_by_clause___0").
-- --------------------------------------------------------------------------------------------------------------------
include "std.arbori"
dummy: :indentConditions & [node) identifier;
skipWhiteSpaceBeforeNode: runOnce -> { var doNotCallCallbackFunction;}
dontFormatNode: [node) numeric_literal | [node) path ->;
-- --------------------------------------------------------------------------------------------------------------------
-- Keep existing whitespace.
-- --------------------------------------------------------------------------------------------------------------------
keep_significant_whitespace:
runOnce
-> {
var LexerToken = Java.type('oracle.dbtools.parser.LexerToken');
var tokens = LexerToken.parse(target.input, true); // include hidden tokens
var hiddenTokenCount = 0;
var wsBefore = "";
var Token = Java.type('oracle.dbtools.parser.Token');
for (var i in tokens) {
var type = tokens[i].type;
if (type == Token.LINE_COMMENT || type == Token.COMMENT || type == Token.WS ||
type == Token.MACRO_SKIP || type == Token.SQLPLUSLINECONTINUE_SKIP)
{
hiddenTokenCount++;
if (type == Token.WS) {
wsBefore += tokens[i].content;
} else {
wsBefore = "";
}
} else {
if (i-hiddenTokenCount == 0 && hiddenTokenCount == wsBefore.length) {
struct.putNewline(0, "");
} else if (wsBefore != " ") {
struct.putNewline(i-hiddenTokenCount, wsBefore);
}
wsBefore = "";
}
}
}
-- --------------------------------------------------------------------------------------------------------------------
-- Enforce nonquoted identifiers.
-- --------------------------------------------------------------------------------------------------------------------
enforce_nonquoted_identifiers:
runOnce
-> {
var offOnRanges = [];
var populateOffOnRanges = function(tokens) {
var off = -1;
for (var i in tokens) {
var type = tokens[i].type;
if (type == Token.LINE_COMMENT || type == Token.COMMENT) {
if (tokens[i].content.toLowerCase().indexOf("@formatter:off") != -1
|| tokens[i].content.toLowerCase().indexOf("noformat start") != -1)
{
off = tokens[i].begin;
}
if (off != -1) {
if (tokens[i].content.toLowerCase().indexOf("@formatter:on") != -1
|| tokens[i].content.toLowerCase().indexOf("noformat end") != -1)
{
offOnRanges.push([off, tokens[i].end]);
off = -1;
}
}
}
}
}
var inOffOnRange = function(pos) {
for (var x in offOnRanges) {
if (pos >= offOnRanges[x][0] && pos < offOnRanges[x][1]) {
return true;
}
}
return false;
}
var HashSet = Java.type('java.util.HashSet');
var Arrays = Java.type('java.util.Arrays');
var reservedKeywords = new HashSet(Arrays.asList("ACCESS","ADD","AFTER","ALL","ALLOW","ALTER","ANALYTIC","AND",
"ANY","ANYSCHEMA","AS","ASC","ASSOCIATE","AUDIT","AUTHID","AUTOMATIC","AUTONOMOUS_TRANSACTION","BEFORE",
"BEGIN","BETWEEN","BULK","BY","BYTE","CANONICAL","CASE","CASE-SENSITIVE","CHAR","CHECK","CLUSTER","COLUMN",
"COLUMN_VALUE","COMMENT","COMPOUND","COMPRESS","CONNECT","CONNECT_BY_ROOT","CONSTANT","CONSTRAINT",
"CONSTRUCTOR","CORRUPT_XID","CORRUPT_XID_ALL","CREATE","CROSSEDITION","CURRENT","CUSTOMDATUM","CYCLE",
"DATE","DB_ROLE_CHANGE","DECIMAL","DECLARE","DECREMENT","DEFAULT","DEFAULTS","DEFINE","DEFINER","DELETE",
"DESC","DETERMINISTIC","DIMENSION","DISALLOW","DISASSOCIATE","DISTINCT","DROP","EACH","EDITIONING","ELSE",
"ELSIF","END","EVALNAME","EXCEPT","EXCEPTION","EXCEPTIONS","EXCEPTION_INIT","EXCLUSIVE","EXISTS","EXTERNAL",
"FETCH","FILE","FLOAT","FOLLOWING","FOLLOWS","FOR","FORALL","FROM","GOTO","GRANT","GROUP","HAVING","HIDE",
"HIER_ANCESTOR","HIER_LAG","HIER_LEAD","HIER_PARENT","IDENTIFIED","IF","IGNORE","IMMEDIATE","IMMUTABLE",
"IN","INCREMENT","INDEX","INDICATOR","INDICES","INITIAL","INITIALLY","INLINE","INSERT","INSTEAD","INTEGER",
"INTERSECT","INTO","INVISIBLE","IS","ISOLATION","JAVA","JSON_EXISTS","JSON_TABLE","LATERAL","LEVEL","LIBRARY",
"LIKE","LIKE2","LIKE4","LIKEC","LOCK","LOGON","LONG","MAXEXTENTS","MAXVALUE","MEASURES","MERGE","MINUS",
"MINVALUE","MLSLABEL","MODE","MODIFY","MULTISET","MUTABLE","NAN","NAV","NCHAR_CS","NESTED_TABLE_ID","NOAUDIT",
"NOCOMPRESS","NOCOPY","NOCYCLE","NONSCHEMA","NORELY","NOT","NOVALIDATE","NOWAIT","NULL","NUMBER","OF",
"OFFLINE","ON","ONLINE","ONLY","OPTION","OR","ORADATA","ORDER","ORDINALITY","OVER","OVERRIDING",
"PARALLEL_ENABLE","PARTITION","PASSING","PAST","PCTFREE","PIPELINED","PIVOT","PRAGMA","PRECEDES",
"PRECEDING","PRESENT","PRIOR","PROCEDURE","PUBLIC","RAW","REFERENCES","REFERENCING","REJECT","RELY",
"RENAME","REPEAT","RESOURCE","RESPECT","RESTRICT_REFERENCES","RESULT_CACHE","RETURNING","REVOKE","ROW",
"ROWID","ROWNUM","ROWS","SELECT","SEQUENTIAL","SERIALIZABLE","SERIALLY_REUSABLE","SERVERERROR","SESSION",
"SET","SETS","SHARE","SIBLINGS","SINGLE","SIZE","SMALLINT","SOME","SQLDATA","SQL_MACRO","STANDALONE",
"START","SUBMULTISET","SUBPARTITION","SUCCESSFUL","SUPPRESSES_WARNING_6009","SYNONYM","SYSDATE","TABLE",
"THE","THEN","TO","TRIGGER","UDF","UID","UNBOUNDED","UNDER","UNION","UNIQUE","UNLIMITED","UNPIVOT","UNTIL",
"UPDATE","UPSERT","USER","USING","VALIDATE","VALUES","VARCHAR","VARCHAR2","VARRAY","VARYING","VIEW",
"WHEN","WHENEVER","WHERE","WHILE","WINDOW","WITH","XMLATTRIBUTES","XMLEXISTS","XMLFOREST","XMLNAMESPACES",
"XMLQUERY","XMLROOT","XMLSCHEMA","XMLSERIALIZE","XMLTABLE"));
var isKeyword = function(token) {
return reservedKeywords.contains(token.content.replace('"', ""));
}
var isUnquotingAllowed = function(token) {
var Pattern = Java.type("java.util.regex.Pattern");
if (!Pattern.matches('^"[A-Z][A-Z0-9_$#]*"$', token.content)) {
return false;
}
if (isKeyword(token)) {
return false;
}
return true;
}
var findAndConvertQuotedIdentifiers = function() {
var tokens = LexerToken.parse(target.input,true); // include hidden tokens
populateOffOnRanges(tokens);
var StringBuilder = Java.type('java.lang.StringBuilder');
var newInput = new StringBuilder(target.input);
var delpos = [];
var conditionalBlock = false;
for (var i in tokens) {
var type = tokens[i].type;
if (type == Token.MACRO_SKIP) {
var content = tokens[i].content.toLowerCase();
if (content.indexOf("$if ") == 0) {
conditionalBlock = true;
} else if (content.indexOf("$end") == 0) {
conditionalBlock = false;
}
}
if (type == Token.DQUOTED_STRING && isUnquotingAllowed(tokens[i])
&& !inOffOnRange(tokens[i].begin) && !conditionalBlock)
{
delpos.push(tokens[i].begin);
delpos.push(tokens[i].end-1);
}
}
var i = delpos.length - 1;
while (i >= 0) {
newInput.deleteCharAt(delpos[i]);
i--;
}
target.input = newInput.toString();
}
var updateParseTreeAndTokenList = function() {
var Parsed = Java.type('oracle.dbtools.parser.Parsed');
var SqlEarley = Java.type('oracle.dbtools.parser.plsql.SqlEarley')
var defaultTokens = LexerToken.parse(target.input);
var newTarget = new Parsed(target.input, defaultTokens, SqlEarley.getInstance(),
Java.to(["sql_statements"], "java.lang.String[]"));
target.src.clear();
target.src.addAll(newTarget.src);
}
// main
findAndConvertQuotedIdentifiers();
updateParseTreeAndTokenList();
}
-- --------------------------------------------------------------------------------------------------------------------
-- Define identifiers (relevant for keyword case and identifier case)
-- --------------------------------------------------------------------------------------------------------------------
analytics: [identifier) identifier & [call) analytic_function & [call = [identifier;
ids: [identifier) identifier;
identifiers: ids - analytics ->;
<options>
<adjustCaseOnly>false</adjustCaseOnly>
<singleLineComments>oracle.dbtools.app.Format.InlineComments.CommentsUnchanged</singleLineComments>
<maxCharLineSize>120000</maxCharLineSize>
<idCase>oracle.dbtools.app.Format.Case.NoCaseChange</idCase>
<kwCase>oracle.dbtools.app.Format.Case.lower</kwCase>
<formatWhenSyntaxError>false</formatWhenSyntaxError>
</options>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment