diff --git a/lib/Controller/DbController.php b/lib/Controller/DbController.php index 2885e24..0d9fe8c 100644 --- a/lib/Controller/DbController.php +++ b/lib/Controller/DbController.php @@ -174,7 +174,7 @@ class DbController extends Controller $stmt = $this->db->prepare($query); $stmt->execute(); - if ($statementType === 'SELECT' || ($statementType === 'WITH' && SqlGuard::isReadOnly($query))) { + if ($statementType === 'SELECT' || ($statementType !== 'SET' && SqlGuard::isReadOnly($query))) { // Read row by row and stop at MAX_ROWS, instead of // fetchAll() + array_slice — otherwise a SELECT without // LIMIT on a huge table would still pull everything into diff --git a/lib/Service/SqlGuard.php b/lib/Service/SqlGuard.php index 6863798..53a6368 100644 --- a/lib/Service/SqlGuard.php +++ b/lib/Service/SqlGuard.php @@ -46,7 +46,7 @@ class SqlGuard private const WRITES_INSIDE_READ = [ '/\b(INSERT|UPDATE|DELETE|MERGE|INTO|ANALYZE)\b/i', '/^SET\s+(SESSION\s+|LOCAL\s+)?(ROLE|SESSION\s+AUTHORIZATION)\b/i', - '/\b(pg_terminate_backend|pg_cancel_backend|pg_reload_conf|set_config|setval|lo_create|lo_put|lo_unlink|pg_switch_wal|pg_create_restore_point|pg_advisory_lock)\s*\(/i', + '/\b(pg_terminate_backend|pg_cancel_backend|pg_reload_conf|set_config|setval|nextval|lo_create|lo_put|lo_unlink|pg_switch_wal|pg_create_restore_point|pg_advisory_lock)\s*\(/i', ]; /** diff --git a/tests/Unit/Service/SqlGuardTest.php b/tests/Unit/Service/SqlGuardTest.php index 27ad54a..23caacc 100644 --- a/tests/Unit/Service/SqlGuardTest.php +++ b/tests/Unit/Service/SqlGuardTest.php @@ -110,6 +110,7 @@ class SqlGuardTest extends TestCase ['SELECT pg_terminate_backend(123)'], ["SELECT set_config('x', 'y', false)"], ["SELECT setval('seq', 1)"], + ["SELECT nextval('seq')"], ['MERGE INTO t USING s ON true WHEN MATCHED THEN DELETE'], ['VACUUM'], ["WITH a AS (SELECT '/*'), b AS (DELETE FROM t RETURNING 1) SELECT '*/'"],