14 Commits

Author SHA1 Message Date
dillard 93221b2905 Also unquote a function name followed by a comment
SELECT "pg_read_file" /* x */ ('/etc/passwd') still passed: the check
for a following parenthesis only skipped whitespace. Skip comments too.
2026-09-30 17:45:09 +02:00
dillard 839572f1fb Only treat --full as the option when it is outside string literals
A value like 'value --full' in the query turned the full table view on.
Leave string literals and quoted identifiers out when looking for the
option.
2026-09-30 17:34:49 +02:00
dillard 266b5a6985 Release 0.4.6
Bump the version, add 0.4.6 to the compatibility matrix and the switch
from occ to the SQL mode as a screenshot.
2026-09-30 17:31:37 +02:00
dillard 3b9fbf42a0 Present the app as an independent project
Rename the README title to Extended OCC Web (occ & SQL terminal), link
the App Store listing, and describe the fork history in the past tense:
the project no longer tracks fanategorius/occweb. Credits stay.
2026-09-30 17:26:35 +02:00
dillard ec4b4a4ece Cut long values in SQL result tables to fit the terminal
One long value made every line of the table wider than the terminal,
so the whole table wrapped. Narrow the widest columns until the table
fits and end cut values with an ellipsis. Adding --full to the query,
which is an SQL comment, shows the values in full as before.
2026-09-30 17:26:16 +02:00
dillard 6dbf2f9675 Ignore trailing comments when splitting SQL batches
A comment after the last semicolon ("SELECT 1; -- note") became a
statement of its own. It is not a read, so the whole batch asked for
confirmation and the comment was then sent to the database. Drop parts
that contain only comments.
2026-09-30 17:26:01 +02:00
dillard d349aad2e6 Show the SQL mode and a command in the screenshots
Replace the empty start screen with a screenshot of the SQL mode and
one of an occ command with its output.
2026-09-30 17:00:00 +02:00
dillard d25580239e Describe the app properly in the app store listing
The summary and description only said "Run OCC Commands in a web
terminal" and did not mention the SQL mode, the confirmation and
blocking rules, the admin-only access or the limitations.
2026-09-30 16:59:01 +02:00
dillard 5c7fbcbbfc Check quoted function names like bare ones in the SQL mode
PostgreSQL accepts a function name in double quotes, so
SELECT "pg_read_file"('/etc/passwd') passed the forbidden-construct
check and SELECT "setval"('seq', 1) skipped the confirmation: the
patterns expect the name right before the parenthesis. The scanner now
unquotes a plain identifier when a parenthesis follows it, so a quoted
call is checked like the bare function name; quoted column names stay
as they are.
2026-09-30 16:56:53 +02:00
dillard d7b093812d Block all dblink functions in the SQL mode
Only dblink() and dblink_connect() were blocked, so dblink_exec(),
dblink_send_query() and the other functions of the extension could
still reach other databases. Match every function whose name starts
with dblink.
2026-09-30 16:48:50 +02:00
dillard 284e75ddb3 Log occ commands for the audit trail
Commands run through the terminal were only logged at debug level, and
admin_audit skipped them because the console request carried no command
in argv. Pass the command as argv so admin_audit records the full
command the way it does for occ on the command line, and log the
command name with the user as a warning, like the SQL mode does for
queries. The regular log gets only the name, because arguments can hold
secrets (config:system:set --value=...).
2026-09-30 16:33:12 +02:00
dillard c1fd53845f Build the console application from the container
The console application was created with a hand-written list of eight
constructor arguments, which broke whenever Nextcloud changed its
constructor. Resolve the arguments by type from the container instead,
passing our own request so the console still gets an argv for its
ConsoleEvent.
2026-09-30 16:11:53 +02:00
dillard 2b57349519 Refresh the screenshots
Replace the old screenshot with a current one of the terminal running
occ, and add the start screen as a second screenshot for the app store.
2026-09-30 16:08:22 +02:00
dillard 45c97653ca Trim the compatibility matrix and add 0.4.5
Keep the last release under the old id and the latest releases; the
older entries stay in the git history of the README.
2026-09-30 16:03:00 +02:00
11 changed files with 147 additions and 46 deletions
+1 -1
View File
@@ -84,7 +84,7 @@ ssh <docker-host> "docker exec -u www-data nextcloud-aio-nextcloud php occ app:d
## `max-version` policy
See the comment next to `<dependencies>` in `appinfo/info.xml`: it must always equal the
highest Nextcloud core version this fork has actually been verified against — never a
highest Nextcloud core version this app has actually been verified against — never a
value inherited from upstream, and never a guess ahead of what's been tested. Test
against the next major's beta ahead of time and bump it before that major goes final,
rather than reacting after the fact.
+24 -20
View File
@@ -1,22 +1,26 @@
# OCCWeb terminal (maintained fork)
# Extended OCC Web (occ & SQL terminal)
### A web terminal for admins to launch Nextcloud's occ commands
### A web terminal for Nextcloud admins to run occ commands and SQL queries
Available in the [Nextcloud App Store](https://apps.nextcloud.com/apps/extended_occweb) as `extended_occweb`.
![occweb](https://github.com/dillardblom/occweb/raw/main/appinfo/screenshot.png)
## About this fork
## History
The original [Adphi/occweb](https://github.com/Adphi/occweb) was marked deprecated by its
author and is unmaintained. This fork tracks [fanategorius/occweb](https://github.com/fanategorius/occweb),
which picked up compatibility maintenance where Adphi left off, and adds SQL query mode,
author and is unmaintained. This project started as a fork of
[fanategorius/occweb](https://github.com/fanategorius/occweb), which had picked up
compatibility maintenance where Adphi left off, and tracked it while adding SQL query mode,
security hardening (admin checks, CSRF/confirmation flows, transactional batches), and PHP
8.1+ compatibility fixes on top.
8.1+ compatibility fixes. Since 0.4.3 it is published under its own app id,
`extended_occweb`, and it is now maintained as an independent project.
### Credits
- [Adphi/occweb](https://github.com/Adphi/occweb) — original author/project.
- [fanategorius/occweb](https://github.com/fanategorius/occweb) — the actively maintained
fork this repo is based on and tracks.
- [fanategorius/occweb](https://github.com/fanategorius/occweb) — the maintained fork this
project was based on.
- [Git-Usr123/occweb](https://github.com/Git-Usr123/occweb) — independent fork; the
`OC::$server->getConfig()` → `OC::$server->get(\OCP\IConfig::class)` fix in
`OccController.php` was spotted there and backported here.
@@ -85,6 +89,10 @@ run the whole block in a single request — this matters for scripts like
statement in one submission runs on the same database connection/session.
Type `occ` to switch back to the normal occ-command mode.
Long values in result tables are cut to fit the terminal. Add `--full` to the
query (it is an SQL comment, so the database ignores it) to see them in full,
e.g. `SELECT configvalue FROM oc_appconfig WHERE configkey = 'x'; --full`.
Every statement that changes data or the schema (`DELETE`, `UPDATE`,
`INSERT`, `DROP`, a data-modifying `WITH`, ...) asks for confirmation before
the batch runs. There is still no undo — double check what you are about to
@@ -170,19 +178,15 @@ was verified, so a rollback target is obvious if a newer major causes problems.
| occweb version | Tag | NC version tested | Result |
|---|---|---|---|
| 0.3.4 | `v0.3.4` | 33.0.8 | ✅ Verified (nc01, 2026-09-25) |
| 0.3.4 | `v0.3.4` | 34.0.4 | ❌ Blank terminal - global jQuery gone on NC34 (`jQuery is not defined`), then a 412 "CSRF check failed" on every ajax call once jQuery was vendored. Fixed in 0.3.5. |
| 0.3.5 | `v0.3.5-nc34` | 34.0.4 | ✅ Verified (nc01, 2026-09-26) - vendors its own jQuery + sends the CSRF `requesttoken` header explicitly on every ajax call |
| 0.3.5 | `v0.3.5-nc34` | 33.0.8 | ⚠️ Believed compatible (the 0.3.5 change is additive-only, nothing removed that NC30-33 depended on) but not re-verified - nc01 no longer runs NC33 to test against |
| 0.4.0 | (untagged) | 34.0.4 | ✅ Verified (nc01, 2026-09-26) - app id renamed `occweb` → `extended_occweb` (App Store prep), no functional changes to the 0.3.5 fixes otherwise |
| 0.4.1 | `v0.4.1-nc34` | 34.0.4 | ✅ Verified (nc01, 2026-09-26) - code review fixes: correct jQuery `.fail()` signature, added a missing `.fail()` handler on the initial terminal-load request |
| 0.4.1 | `v0.4.1-nc34` | 33.0.8 | ⚠️ Same caveat as 0.3.5/0.4.0 - not re-verified, no NC33 test environment available. Also unresolved: `js/jquery.js` now loads unconditionally on every supported version, even NC30-33 where core still provides jQuery - flagged in code review, needs an NC33 environment to confirm it's harmless there. |
| 0.4.2 | (untagged) | 32.0.15 | ✅ Verified (`testbak/`, 2026-09-27) - terminal loads and runs commands correctly. Confirms the suspected jQuery issue above: core still exposes a read-only `window.$` getter here, so our vendored `js/jquery.js` throws `TypeError: Cannot set property $ of #<Window> which has only a getter` on load - a real, uncaught console exception, but harmless in practice since the app attaches via `window.jQuery` (still writable), not `window.$`. Fixed in 0.4.3. |
| 0.4.2 | (untagged) | 33.0.9 | ✅ Verified (`testbak/`, 2026-09-27) - same result as 32.0.15: functional, same harmless-but-real `window.$` console exception. Fixed in 0.4.3. |
| 0.4.2 | (untagged) | 35.0.1 | ✅ Verified (`testbak/`, 2026-09-27) after a real fix: the bootstrap request 500'd with a PHP fatal, `Declaration of OccOutput::setErrorOutput() must be compatible with ConsoleOutputInterface::setErrorOutput(): void` - NC35 bundles a Symfony Console version whose interface now declares that return type. Fixed by adding `: void` to `OccOutput::setErrorOutput()` (`lib/Controller/OccOutput.php`), which is compatible with every earlier Symfony/NC version too. Confirmed clean afterwards: no console errors, no CSRF issues, terminal fully functional. |
| 0.4.3 | (untagged) | 32.0.15, 34.0.4 | ✅ Verified (`testbak/`, 2026-09-27) - `templates/index.php` now only loads `js/jquery.js` when `\OCP\ServerVersion::getMajorVersion() >= 34`, since core still provides a working jQuery on 32/33. Re-tested both sides of the boundary: NC32 loads no `jquery.js` request at all and has zero console errors; NC34 still loads it and still works, confirming no regression. |
| 0.3.4 | `v0.3.4` | 33.0.8 | ✅ Verified (2026-09-25). Last release under the old `occweb` id; does not work on NC34 (missing jQuery, CSRF), fixed from 0.3.5 on. |
| 0.4.3 | `v0.4.3` | 32.0.15, 33.0.9, 34.0.4, 35.0.1 | ✅ Verified (`testbak/`, 2026-09-27). First release under `extended_occweb`: NC35 fatal in `OccOutput` fixed, vendored jQuery only loaded on NC34+. |
| 0.4.4 | (untagged) | 34.0.4 | ✅ Verified (2026-09-30). Brackets in occ output escaped before rendering. |
| 0.4.5 | `v0.4.5` | 32.0.15, 33.0.9, 34.0.4, 35.0.1 | ✅ Verified (`testbak/`, 2026-09-30). SQL mode hardening, occ output sanitized, first release code-signed with the Nextcloud certificate. |
| 0.4.6 | `v0.4.6` | 32.0.15, 33.0.9, 34.0.4, 35.0.1 | ✅ Verified (`testbak/`, 2026-09-30). occ commands logged for the audit trail, console built from the container, more SQL guard gaps closed, long values in result tables cut to fit (`--full` shows them). |
`min-version`/`max-version` in `appinfo/info.xml` were updated from `30`/`34` to `32`/`35` on 2026-09-27 based on the above: 30/31 have never actually been tested (not even before the app-id rename) so claiming them was never justified once the `testbak/`-based verification policy existed, and 35 is now a genuinely verified (and fixed) target rather than a guess. 30/31 are also past Nextcloud's own currently-supported boundary as of 2026-09-18 (32-35 supported), which independently confirms 32 as the right floor.
Older entries (0.3.5-0.4.2) are in the git history of this file.
`min-version`/`max-version` in `appinfo/info.xml` are `32`/`35` since 2026-09-27: 30/31 were never tested and are past Nextcloud's supported range, and 35 is verified rather than guessed.
If a future major breaks something, `v0.3.4` is the last version confirmed working on NC33
and older - but a plain `git checkout v0.3.4` is **not** a working rollback by itself. That
+28 -4
View File
@@ -3,9 +3,30 @@
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>extended_occweb</id>
<name>Extended OCC Web</name>
<summary>OCC Commands in a web terminal</summary>
<description><![CDATA[Run OCC Commands in a web terminal]]></description>
<version>0.4.5</version>
<summary>Run occ commands and SQL queries from a web terminal (admins only)</summary>
<description><![CDATA[A terminal in the browser for Nextcloud administrators: run occ commands and query the database without shell access to the server. Handy on Nextcloud All-in-One (AIO) and other container setups, where occ otherwise needs `docker exec`.
**occ mode**
- Run any occ command, with tab completion of command names and colored output.
- Every command is logged with the user who ran it, in the Nextcloud log and, when enabled, in the admin_audit log.
**SQL mode** (type `sql`)
- Run SQL against the Nextcloud database. Statements separated by `;` run as one batch on one connection, and the batch is rolled back if a statement fails.
- Results are shown as tables (up to 1000 rows per query).
- Statements that change data or the schema ask for confirmation before the batch runs. Reading or writing files on the database server and starting programs through SQL are blocked.
- Ready-made scripts: type `templates`, for example to look up or completely delete a local user. A template only fills the command line; nothing runs until you review it and press Enter.
**Security**
- Only members of the Nextcloud admin group can use the app; every request is checked and CSRF-protected.
- Treat access to it like shell access to the server, and run the database as a user without superuser or server file/program rights. See the Security section in the README.
**Limitations**
- Not an interactive terminal: long-running commands (like `files:scan` on a large instance) time out, and commands that ask questions cannot be answered (use `-n`).]]></description>
<version>0.4.6</version>
<licence>agpl</licence>
<author mail="dillard.blom@ensembia.com" >Dillard Blom</author>
<namespace>OCCWeb</namespace>
@@ -14,9 +35,12 @@
<bugs>https://github.com/dillardblom/occweb/issues</bugs>
<repository>https://github.com/dillardblom/occweb</repository>
<screenshot>https://github.com/dillardblom/occweb/raw/main/appinfo/screenshot.png</screenshot>
<screenshot>https://github.com/dillardblom/occweb/raw/main/appinfo/screenshot-status.png</screenshot>
<screenshot>https://github.com/dillardblom/occweb/raw/main/appinfo/screenshot-sql-switch.png</screenshot>
<screenshot>https://github.com/dillardblom/occweb/raw/main/appinfo/screenshot-sql.png</screenshot>
<dependencies>
<!-- min-version and max-version must equal the lowest/highest NC core
versions this fork has actually been verified on (via testbak/,
versions this app has actually been verified on (via testbak/,
see its README) - never inherited from upstream or guessed at
either end. min-version was 30 pre-testbak, unverified since the
app-id rename/jQuery fixes - narrowed to 32 (2026-09-27) once 30/31
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

After

Width:  |  Height:  |  Size: 101 KiB

+38 -6
View File
@@ -203,7 +203,10 @@
}
// Tabular rendering of SELECT results, psql-style.
function renderTable(term, rows) {
// Unless full is set, the widest columns are narrowed until the table
// fits the terminal, so one long value doesn't wrap the whole table.
// Returns true when a value was cut.
function renderTable(term, rows, full) {
if (!rows || !rows.length) {
return;
}
@@ -234,9 +237,34 @@
}, col.length);
});
var MIN_WIDTH = 10;
var cut = false;
if (!full) {
var available = term.cols() - 1;
var total = function () {
return widths.reduce(function (sum, w) { return sum + w + 3; }, -1);
};
while (total() > available) {
var widest = widths.indexOf(Math.max.apply(null, widths));
if (widths[widest] <= MIN_WIDTH) {
break;
}
widths[widest] = Math.max(MIN_WIDTH, widths[widest] - (total() - available));
}
}
function fit(str, width) {
str = String(str);
if (str.length <= width) {
return str;
}
cut = true;
return str.slice(0, width - 1) + '\u2026';
}
function formatRow(cells) {
return cells.map(function (cell, i) {
return ' ' + pad(cell, widths[i]) + ' ';
return ' ' + pad(fit(cell, widths[i]), widths[i]) + ' ';
}).join('|');
}
@@ -248,9 +276,10 @@
});
term.echo($.terminal.escape_formatting(lines.join('\n')));
return cut;
}
function renderSqlResponse(term, response) {
function renderSqlResponse(term, response, full) {
if (!response) {
term.echo('[[;#ff5555;]Empty response from server]');
return;
@@ -270,8 +299,8 @@
term.echo('[[;#ff5555;] Error: ]' + $.terminal.escape_formatting(r.error || 'unknown error'));
} else if (r.type === 'select') {
term.echo('[[;gray;] ' + r.count + ' row(s)]');
if (r.count > 0) {
renderTable(term, r.data);
if (r.count > 0 && renderTable(term, r.data, full)) {
term.echo('[[;gray;] Long values are cut to fit the terminal. Add --full to the query to see them in full.]');
}
if (r.truncated) {
term.echo('[[;yellow;] Result truncated — showing only the first ' + r.count + ' rows, add LIMIT to see more precisely.]');
@@ -327,7 +356,10 @@
askDeleteConfirmation(term, sql, response.error);
return;
}
renderSqlResponse(term, response);
// "--full" is a plain SQL comment, so the database ignores it; string
// literals are left out so a value containing it doesn't count
var withoutStrings = sql.replace(/'(?:[^']|'')*'/g, "''").replace(/"(?:[^"]|"")*"/g, '""');
renderSqlResponse(term, response, /(^|\s)-- ?full\b/.test(withoutStrings));
term.resume();
}).fail(function (xhr, status) {
if (status === 'timeout') {
+30 -12
View File
@@ -5,7 +5,6 @@ namespace OCA\OCCWeb\Controller;
use Exception;
use OC;
use OC\Console\Application;
use OC\MemoryInfo;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\DataResponse;
@@ -21,6 +20,7 @@ class OccController extends Controller
private $userId;
private $application;
private $fakeRequest;
private $symphonyApplication;
private $output;
@@ -30,16 +30,8 @@ class OccController extends Controller
$this->logger = OC::$server->get(LoggerInterface::class);
$this->userId = $userId;
$this->application = new Application(
OC::$server->get(\OCP\ServerVersion::class),
OC::$server->get(\OCP\IConfig::class),
OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class),
new FakeRequest(),
$this->logger,
OC::$server->query(MemoryInfo::class),
OC::$server->get(\OCP\App\IAppManager::class), // Obtain the IAppManager
OC::$server->get(\OCP\Defaults::class)
);
$this->fakeRequest = new FakeRequest();
$this->application = $this->createConsoleApplication($this->fakeRequest);
$this->application->setAutoExit(false);
$this->output = new OccOutput(OutputInterface::VERBOSITY_NORMAL, true);
$this->application->loadCommands(new StringInput(""), $this->output);
@@ -82,6 +74,24 @@ class OccController extends Controller
return new TemplateResponse('extended_occweb', 'index');
}
/**
* Builds Nextcloud's console application with its dependencies from the
* container, except the request: the console reads argv from it for the
* ConsoleEvent (used by admin_audit), which a web request doesn't have.
* Resolving the arguments by type keeps this working when Nextcloud
* changes the constructor, which it has done several times.
*/
private function createConsoleApplication(IRequest $request): Application
{
$arguments = [];
foreach ((new \ReflectionClass(Application::class))->getConstructor()->getParameters() as $parameter) {
$type = $parameter->getType();
$class = $type instanceof \ReflectionNamedType ? $type->getName() : null;
$arguments[] = $class === IRequest::class ? $request : OC::$server->get($class);
}
return new Application(...$arguments);
}
/**
* @param $input
* @return string
@@ -104,7 +114,15 @@ class OccController extends Controller
public function cmd($command)
{
if ($err = $this->requireAdmin()) return $err;
$this->logger->debug($command);
// The regular log gets only the command name: arguments can hold
// secrets (config:system:set --value=...). The full command goes to
// admin_audit through the ConsoleEvent, like occ on the CLI.
$this->logger->warning('[extended_occweb] occ command by {user}: {command}', [
'app' => 'extended_occweb',
'user' => $this->userId,
'command' => strtok(trim((string) $command), " \t\n") ?: '(empty)',
]);
$this->fakeRequest->server['argv'] = ['occ', $command];
$input = new StringInput($command);
$response = $this->run($input);
$this->logger->debug($response);
+11 -3
View File
@@ -27,7 +27,7 @@ class SqlGuard
'/\bpg_stat_file\s*\(/i' => 'pg_stat_file()',
'/\blo_import\s*\(/i' => 'lo_import()',
'/\blo_export\s*\(/i' => 'lo_export()',
'/\bdblink(_connect)?\s*\(/i' => 'dblink() (connects to arbitrary databases)',
'/\bdblink\w*\s*\(/i' => 'dblink functions (connect to arbitrary databases)',
'/\bLOAD_FILE\s*\(/i' => 'LOAD_FILE()',
'/\bINTO\s+(OUTFILE|DUMPFILE)\b/i' => 'INTO OUTFILE/DUMPFILE',
];
@@ -87,8 +87,9 @@ class SqlGuard
$statements[] = trim($current);
// A trailing comment ("SELECT 1; -- note") is not a statement
return array_values(array_filter($statements, function ($s) {
return $s !== '';
return trim(self::removeComments($s)) !== '';
}));
}
@@ -122,7 +123,14 @@ class SqlGuard
while ($i < $len) {
$end = self::literalEnd($sql, $i);
if ($end !== null) {
$result .= $replaceLiterals ? ($sql[$i] === '"' ? '""' : "''") : substr($sql, $i, $end - $i);
$literal = substr($sql, $i, $end - $i);
if (preg_match('/^"([A-Za-z_][A-Za-z0-9_$]*)"$/', $literal, $m) && preg_match('/\G(?:\s|--[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*\(/', $sql, $unused, 0, $end)) {
// A quoted function name ("setval"(...)) calls the same
// function as the bare word, so check it as one
$result .= $m[1];
} else {
$result .= $replaceLiterals ? ($sql[$i] === '"' ? '""' : "''") : $literal;
}
$i = $end;
continue;
}
+15
View File
@@ -23,6 +23,13 @@ class SqlGuardTest extends TestCase
["SELECT pg_ls_dir('/')"],
["SELECT lo_import('/etc/passwd')"],
["SELECT dblink_connect('host=x')"],
["SELECT dblink_exec('host=x', 'DROP TABLE t')"],
["SELECT \"pg_read_file\"('/etc/passwd')"],
["SELECT pg_catalog.\"pg_read_file\"('/etc/passwd')"],
["SELECT \"pg_read_file\" /* x */ ('/etc/passwd')"],
["SELECT \"pg_read_file\"-- x\n('/etc/passwd')"],
["SELECT dblink_send_query('c', 'SELECT 1')"],
["SELECT * FROM dblink('host=x', 'SELECT 1') AS t(a int)"],
["SELECT * FROM t INTO OUTFILE '/tmp/x'"],
["COPY (SELECT '/*') TO PROGRAM 'sh' --*/"],
["COPY (SELECT '--') TO PROGRAM 'sh'"],
@@ -119,6 +126,10 @@ class SqlGuardTest extends TestCase
["SELECT set_config('x', 'y', false)"],
["SELECT setval('seq', 1)"],
["SELECT nextval('seq')"],
["SELECT \"setval\"('seq', 1)"],
["SELECT pg_catalog.\"setval\"('seq', 1)"],
["SELECT pg_catalog.setval('seq', 1)"],
["SELECT \"setval\" /* x */ ('seq', 1)"],
['MERGE INTO t USING s ON true WHEN MATCHED THEN DELETE'],
['VACUUM'],
['PRAGMA journal_mode = WAL'],
@@ -159,6 +170,10 @@ class SqlGuardTest extends TestCase
["/* it's; here */ SELECT 1; SELECT 2", ["/* it's; here */ SELECT 1", 'SELECT 2']],
['SELECT \$1; SELECT 2', ['SELECT \$1', 'SELECT 2']],
[' ; ;SELECT 1;', ['SELECT 1']],
["SELECT 1; --full", ['SELECT 1']],
["SELECT 1; -- note\nSELECT 2", ['SELECT 1', "-- note\nSELECT 2"]],
["-- only a comment", []],
["SELECT 1; /* note */", ['SELECT 1']],
];
}