Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 6607aaa

Browse files
Merge pull request #247 from Trivadis/feature/issue-244-timeout
Add timeout parameter in standalone formatter
2 parents be78c82 + 3f95832 commit 6607aaa

File tree

12 files changed

+255
-40
lines changed

12 files changed

+255
-40
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ This repository provides formatter settings for the [coding style rules](https:/
66

77
Settings are primarily provided for
88

9-
- [Oracle SQLcl, Version 22.3.0](https://www.oracle.com/tools/downloads/sqlcl-downloads.html)
10-
- [Oracle SQL Developer, Version 22.2.1](https://www.oracle.com/tools/downloads/sqldev-downloads.html) (requires `dbtools-common.jar` from SQLcl 22.3.0)
9+
- [Oracle SQLcl, Version 22.4.0](https://www.oracle.com/tools/downloads/sqlcl-downloads.html)
10+
- [Oracle SQL Developer, Version 22.2.1](https://www.oracle.com/tools/downloads/sqldev-downloads.html) (requires `dbtools-common.jar` from SQLcl 22.4.0, however, this breaks the code completion feature)
1111

1212
These settings have been defined and tested with the product versions mentioned above. They might not work in other versions.
1313

settings/sql_developer/trivadis_custom_format.arbori

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
include "std.arbori"
1818

1919
/**
20-
* Lightweight Formatter for SQL Developer and SQLcl, version 22.3.1-SNAPSHOT
20+
* Lightweight Formatter for SQL Developer and SQLcl, version 22.4.0-SNAPSHOT
2121
* The idea is to keep the code formatted "as is" and apply chosen formatting rules only.
2222
*
2323
* The Arbori program is processed from top to bottom.

sqlcl/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ You can apply the formatter settings in SQL Developer. Another option is to appl
66

77
## Why not Use `FORMAT FILE`?
88

9-
Yes, you can use the built-in `FORMAT FILE` command. Here's the help output from SQLcl 22.3.0:
9+
Yes, you can use the built-in `FORMAT FILE` command. Here's the help output from SQLcl 22.4.0:
1010

1111
```
1212
FORMAT
@@ -28,7 +28,7 @@ We recommend you download, clone, or fork this repository when you plan to use [
2828
However, [`format.js`](format.js) also works as a standalone script. Here's the usage:
2929

3030
```
31-
Trivadis PL/SQL & SQL Formatter (format.js), version 22.3.0
31+
Trivadis PL/SQL & SQL Formatter (format.js), version 22.4.0
3232
3333
usage: script format.js <rootPath> [options]
3434
@@ -69,7 +69,7 @@ script (...)/plsql-formatter-settings/sqlcl/format.js --register
6969
Afterwards you can type `tvdformat` to get this usage help:
7070

7171
```
72-
Trivadis PL/SQL & SQL Formatter (tvdformat), version 22.2.1
72+
Trivadis PL/SQL & SQL Formatter (tvdformat), version 22.4.0
7373
7474
usage: tvdformat <rootPath> [options]
7575

sqlcl/format.js

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var javaSqlEarley = Java.type("oracle.dbtools.parser.plsql.SqlEarley");
5151
var javaProgram = Java.type("oracle.dbtools.arbori.Program");
5252

5353
var getVersion = function() {
54-
return "22.3.1-SNAPSHOT";
54+
return "22.4.0-SNAPSHOT";
5555
}
5656

5757
var getFiles = function (rootPath, extensions, ignoreMatcher) {
@@ -162,19 +162,39 @@ var getConfiguredFormatter = function (xmlPath, arboriPath) {
162162
return formatter;
163163
}
164164

165+
var formatInSandbox = function(formatter, original, timeout) {
166+
if (timeout === 0) {
167+
// run formatter without timeout
168+
return formatter.format(original);
169+
} else {
170+
// run formatter in a thread with a given timeout in seconds
171+
// requires SandboxedFormatter which is not available when running within SQLcl
172+
var javaSandboxedFormatter = Java.type("com.trivadis.plsql.formatter.sandbox.SandboxedFormatter");
173+
return javaSandboxedFormatter.format(formatter, original, timeout);
174+
}
175+
}
176+
165177
var hasParseErrors = function (content, consoleOutput) {
166178
var newContent = "\n" + content; // ensure correct line number in case of an error
167179
var tokens = javaLexer.parse(newContent);
168-
var parsed = new javaParsed(newContent, tokens, javaSqlEarley.getInstance(), Java.to(["sql_statements"], "java.lang.String[]"));
169-
var syntaxError = parsed.getSyntaxError();
170-
if (syntaxError != null && syntaxError.getMessage() != null) {
180+
try {
181+
var parsed = new javaParsed(newContent, tokens, javaSqlEarley.getInstance(), Java.to(["sql_statements"], "java.lang.String[]"));
182+
var syntaxError = parsed.getSyntaxError();
183+
if (syntaxError != null && syntaxError.getMessage() != null) {
184+
if (consoleOutput) {
185+
ctx.write(syntaxError.getDetailedMessage());
186+
ctx.write("... ");
187+
}
188+
return true;
189+
}
190+
return false;
191+
} catch (e) {
171192
if (consoleOutput) {
172-
ctx.write(syntaxError.getDetailedMessage());
193+
ctx.write("Internal during parse: " + e.getMessage());
173194
ctx.write("... ");
174195
}
175196
return true;
176197
}
177-
return false;
178198
}
179199

180200
var readFile = function (file) {
@@ -236,6 +256,10 @@ var printUsage = function (asCommand, standalone) {
236256
ctx.write(" serr=all reports all syntax errors\n");
237257
ctx.write(" serr=ext reports syntax errors for files defined with ext option\n");
238258
ctx.write(" serr=mext reports syntax errors for files defined with mext option\n");
259+
if (standalone) {
260+
ctx.write(" timeout=<sec> time in seconds to wait for the completion of the formatting for a file.\n");
261+
ctx.write(" the default value is 0 seconds, which means no timeout.\n");
262+
}
239263
ctx.write(" --help, -h, print this help screen and exit\n")
240264
ctx.write(" --version, -v print version and exit\n")
241265
if (!asCommand && !standalone) {
@@ -310,6 +334,7 @@ var processAndValidateArgs = function (args) {
310334
var ignorePath = null;
311335
var ignoreMatcher = null;
312336
var serr = null;
337+
var timeout = null;
313338
var files = [];
314339
var result = function (valid) {
315340
return {
@@ -321,6 +346,7 @@ var processAndValidateArgs = function (args) {
321346
arboriPath: arboriPath,
322347
ignoreMatcher: ignoreMatcher,
323348
serr: serr,
349+
timeout: timeout,
324350
valid: valid
325351
};
326352
}
@@ -383,6 +409,9 @@ var processAndValidateArgs = function (args) {
383409
if (typeof configJson.serr !== 'undefined') {
384410
serr = configJson.serr.toLowerCase();
385411
}
412+
if (typeof configJson.timeout !== 'undefined') {
413+
timeout = configJson.timeout;
414+
}
386415
if (typeof configJson.files !== 'undefined') {
387416
if (!Array.isArray(configJson.files)) {
388417
ctx.write("files in " + rootPath + " is not an array.\n\n");
@@ -447,6 +476,10 @@ var processAndValidateArgs = function (args) {
447476
serr = args[i].substring(5).toLowerCase();
448477
continue;
449478
}
479+
if (args[i].toLowerCase().indexOf("timeout=") === 0) {
480+
timeout = args[i].substring(8);
481+
continue;
482+
}
450483
ctx.write("invalid argument " + args[i] + ".\n\n");
451484
return result(false);
452485
}
@@ -506,17 +539,35 @@ var processAndValidateArgs = function (args) {
506539
return result(false);
507540
}
508541
}
542+
if (timeout == null) {
543+
timeout = 0;
544+
} else {
545+
if (isNaN(timeout)) {
546+
ctx.write("timeout must be a number.\n\n");
547+
return result(false);
548+
}
549+
timeout = Number(timeout);
550+
if (timeout < 0) {
551+
ctx.write("timeout cannot be less than zero.\n\n");
552+
return result(false);
553+
}
554+
var standalone = javaSystem.getProperty('tvdformat.standalone') != null;
555+
if (!standalone) {
556+
ctx.write("timeout is not supported in SQLcl, use the standalone formatter tvdformat.\n\n")
557+
return result(false);
558+
}
559+
}
509560
return result(true);
510561
}
511562

512-
var formatBuffer = function (formatter) {
563+
var formatBuffer = function (formatter, timeout) {
513564
ctx.write("Formatting SQLcl buffer... ");
514565
ctx.getOutputStream().flush();
515566
var original = ctx.getSQLPlusBuffer().getBufferSafe().getBuffer();
516567
if (hasParseErrors(original, true)) {
517568
ctx.write("skipped.\n");
518569
} else {
519-
var formatted = javaArrays.asList(formatter.format(original).split("\n"));
570+
var formatted = javaArrays.asList(formatInSandbox(formatter, original, timeout).split("\n"));
520571
ctx.getSQLPlusBuffer().getBufferSafe().resetBuffer(formatted);
521572
ctx.write("done.\n");
522573
ctx.write(ctx.getSQLPlusBuffer().getBufferSafe().list(false));
@@ -552,7 +603,7 @@ var detectCharset = function(content) {
552603
return null;
553604
}
554605

555-
var formatMarkdownFile = function (file, formatter, serr) {
606+
var formatMarkdownFile = function (file, formatter, serr, timeout) {
556607
var bytes = javaFiles.readAllBytes(file);
557608
var charset = detectCharset(bytes);
558609
if (charset == null) {
@@ -577,7 +628,7 @@ var formatMarkdownFile = function (file, formatter, serr) {
577628
result += original.substring(m.start(2), m.end(3));
578629
} else {
579630
ctx.write("done... ")
580-
result += formatter.format(m.group(2));
631+
result += formatInSandbox(formatter, m.group(2), timeout);
581632
result += original.substring(m.end(2), m.end(3));
582633
}
583634
pos = m.end(3);
@@ -602,7 +653,7 @@ var getLineSeparator = function (input) {
602653
return lineSep;
603654
}
604655

605-
var formatFile = function (file, formatter, serr) {
656+
var formatFile = function (file, formatter, serr, timeout) {
606657
var bytes = javaFiles.readAllBytes(file);
607658
var charset = detectCharset(bytes);
608659
if (charset == null) {
@@ -616,20 +667,20 @@ var formatFile = function (file, formatter, serr) {
616667
if (hasParseErrors(original, consoleOutput)) {
617668
ctx.write("skipped.\n");
618669
} else {
619-
writeFile(file, formatter.format(original) + getLineSeparator(original), charset);
670+
writeFile(file, formatInSandbox(formatter, original, timeout) + getLineSeparator(original), charset);
620671
ctx.write("done.\n");
621672
}
622673
}
623674
}
624675

625-
var formatFiles = function (files, formatter, markdownExtensions, serr) {
676+
var formatFiles = function (files, formatter, markdownExtensions, serr, timeout) {
626677
for (var i = 0; i < files.length; i++) {
627678
ctx.write("Formatting file " + (i + 1) + " of " + files.length + ": " + files[i].toString() + "... ");
628679
ctx.getOutputStream().flush();
629680
if (isMarkdownFile(files[i], markdownExtensions)) {
630-
formatMarkdownFile(files[i], formatter, serr);
681+
formatMarkdownFile(files[i], formatter, serr, timeout);
631682
} else {
632-
formatFile(files[i], formatter, serr);
683+
formatFile(files[i], formatter, serr, timeout);
633684
}
634685
ctx.getOutputStream().flush();
635686
}
@@ -675,15 +726,15 @@ var run = function (args) {
675726
} else {
676727
var formatter = getConfiguredFormatter(options.xmlPath, options.arboriPath);
677728
if (options.rootPath === "*") {
678-
formatBuffer(formatter);
729+
formatBuffer(formatter, options.timeout);
679730
} else {
680731
var files;
681732
if (options.files.length > 0) {
682733
files = getRelevantFiles(options.files, options.extensions, options.ignoreMatcher);
683734
} else {
684735
files = getFiles(options.rootPath, options.extensions, options.ignoreMatcher);
685736
}
686-
formatFiles(files, formatter, options.markdownExtensions, options.serr);
737+
formatFiles(files, formatter, options.markdownExtensions, options.serr, options.timeout);
687738
}
688739
}
689740
}

standalone/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ The parameters are the same as for the [executable JAR](#executable-jar).
5151

5252
## How to Build
5353

54-
1. [Download](https://www.oracle.com/tools/downloads/sqlcl-downloads.html) and install SQLcl 22.3.0
55-
2. [Download](https://github.com/graalvm/graalvm-ce-builds/releases) and install the GraalVM JDK 17 22.2.0
56-
3. Go to the `bin` directory of the GraalVM JDK and run `./gu install js native-image`, if you want to produce a native image. For native image on Windows you need to [download](https://visualstudio.microsoft.com/downloads/) Visual Studio Community 2022 and install the C++ compiler. Use `x64 Native Tools Command Prompt for VS 2022` to get a terminal window with the correct environment.
57-
4. [Download](https://maven.apache.org/download.cgi) and install Apache Maven 3.8.6
54+
1. [Download](https://www.oracle.com/tools/downloads/sqlcl-downloads.html) and install SQLcl 22.4.0
55+
2. [Download](https://github.com/graalvm/graalvm-ce-builds/releases) and install the GraalVM JDK 17 22.3.1
56+
3. Go to the `bin` directory of the GraalVM JDK and run `./gu install js native-image visualvm`. For native image on Windows you need to [download](https://visualstudio.microsoft.com/downloads/) Visual Studio Community 2022 and install the C++ compiler. Use `x64 Native Tools Command Prompt for VS 2022` to get a terminal window with the correct environment.
57+
4. [Download](https://maven.apache.org/download.cgi) and install Apache Maven 3.9.1
5858
5. [Download](https://git-scm.com/downloads) and install a git command line client
5959
6. Clone the plsql-formatter-settings repository. The repository uses symbolic links. On Windows you have to use `git clone -c core.symlinks=true https://github.com/Trivadis/plsql-formatter-settings.git` as Administrator to make it work. See also [Symbolic Links in Windows](https://github.com/git-for-windows/git/wiki/Symbolic-Links) for more information.
6060
7. Open a terminal window in the plsql-formatter-settings root folder and type

standalone/install_sqlcl_libs.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ if ! test -f "${SQLCL_LIBDIR}/dbtools-common.jar"; then
1515
fi
1616

1717
# define common Maven properties
18-
SQLCL_VERSION="22.3.0"
18+
SQLCL_VERSION="22.4.0"
1919

2020
# install JAR files in local Maven repository, these libs are not available in public Maven repositories
2121
mvn install:install-file -Dfile=$SQLCL_LIBDIR/dbtools-common.jar \

standalone/pom.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
<!-- The Basics -->
77
<groupId>com.trivadis.plsql.formatter</groupId>
88
<artifactId>tvdformat</artifactId>
9-
<version>22.3.1-SNAPSHOT</version>
9+
<version>22.4.1-SNAPSHOT</version>
1010
<packaging>jar</packaging>
1111
<properties>
1212
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1313
<jdk.version>11</jdk.version>
1414
<jdk.test.version>17</jdk.test.version>
15-
<sqlcl.version>22.3.0</sqlcl.version>
16-
<graalvm.version>22.2.0</graalvm.version>
17-
<native.maven.plugin.version>0.9.13</native.maven.plugin.version>
15+
<sqlcl.version>22.4.0</sqlcl.version>
16+
<graalvm.version>22.3.1</graalvm.version>
17+
<native.maven.plugin.version>0.9.20</native.maven.plugin.version>
1818
<reflections.version>0.10.2</reflections.version>
19-
<slf4j.version>1.7.36</slf4j.version>
19+
<slf4j.version>2.0.7</slf4j.version>
2020
<javassist.version>3.29.2-GA</javassist.version>
2121
<skip.native>true</skip.native>
2222
<disable.logging>true</disable.logging>
@@ -82,7 +82,7 @@
8282
<dependency>
8383
<groupId>org.junit.jupiter</groupId>
8484
<artifactId>junit-jupiter</artifactId>
85-
<version>5.9.0</version>
85+
<version>5.9.2</version>
8686
<scope>test</scope>
8787
</dependency>
8888
<dependency>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.trivadis.plsql.formatter.sandbox;
2+
3+
import oracle.dbtools.app.Format;
4+
5+
import java.util.concurrent.ExecutionException;
6+
import java.util.concurrent.ExecutorService;
7+
import java.util.concurrent.Executors;
8+
import java.util.concurrent.Future;
9+
import java.util.concurrent.TimeUnit;
10+
import java.util.concurrent.TimeoutException;
11+
12+
public class SandboxedFormatter {
13+
public static Format copy(Format original) {
14+
Format formatter = new Format();
15+
formatter.options.putAll(original.options);
16+
return formatter;
17+
}
18+
19+
public static String format(Format formatter, String original, Integer timeout) {
20+
ExecutorService executor = Executors.newSingleThreadExecutor();
21+
Format newFormatter = formatter;
22+
if (formatter.options.containsKey("timeout")) {
23+
// it is costly to create a new formatter, so we do that only when needed.
24+
formatter.options.remove("timeout");
25+
newFormatter = copy(formatter);
26+
}
27+
Future<String> future = executor.submit(new SandboxedFormatterTask(newFormatter, original));
28+
String result;
29+
try {
30+
result = future.get(timeout, TimeUnit.SECONDS);
31+
} catch (TimeoutException | InterruptedException | ExecutionException e) {
32+
future.cancel(true);
33+
System.out.print("timeout... ");
34+
formatter.options.put("timeout", true);
35+
result = original;
36+
}
37+
executor.shutdown();
38+
return result;
39+
}
40+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.trivadis.plsql.formatter.sandbox;
2+
3+
import oracle.dbtools.app.Format;
4+
5+
import java.util.concurrent.Callable;
6+
7+
public class SandboxedFormatterTask implements Callable<String> {
8+
final Format formatter;
9+
final String original;
10+
11+
public SandboxedFormatterTask(Format formatter, String original) {
12+
this.formatter = formatter;
13+
this.original = original;
14+
}
15+
16+
@Override
17+
public String call() throws Exception {
18+
return formatter.format(original);
19+
}
20+
}

0 commit comments

Comments
 (0)