@@ -557,3 +557,176 @@ test('return interval as string', async () => {
557557 ]
558558 ` )
559559} )
560+
561+ test ( 'line position error' , async ( ) => {
562+ const res = await app . inject ( {
563+ method : 'POST' ,
564+ path : '/query' ,
565+ payload : { query : 'SELECT *\nFROM pg_class\nWHERE relname = missing_quotes;' } ,
566+ } )
567+ expect ( res . json ( ) ) . toMatchInlineSnapshot ( `
568+ {
569+ "code": "42703",
570+ "error": "ERROR: 42703: column "missing_quotes" does not exist
571+ LINE 3: WHERE relname = missing_quotes;
572+ ^
573+ ",
574+ "file": "parse_relation.c",
575+ "formattedError": "ERROR: 42703: column "missing_quotes" does not exist
576+ LINE 3: WHERE relname = missing_quotes;
577+ ^
578+ ",
579+ "length": 114,
580+ "line": "3589",
581+ "message": "column "missing_quotes" does not exist",
582+ "name": "error",
583+ "position": "40",
584+ "routine": "errorMissingColumn",
585+ "severity": "ERROR",
586+ }
587+ ` )
588+ } )
589+
590+ test ( 'error with additional details' , async ( ) => {
591+ // This query will generate an error with details
592+ const res = await app . inject ( {
593+ method : 'POST' ,
594+ path : '/query' ,
595+ payload : {
596+ query : `DO $$
597+ DECLARE
598+ my_var int;
599+ BEGIN
600+ -- This will trigger an error with detail, hint, and context
601+ SELECT * INTO STRICT my_var FROM (VALUES (1), (2)) AS t(v);
602+ END $$;` ,
603+ } ,
604+ } )
605+
606+ expect ( res . json ( ) ) . toMatchInlineSnapshot ( `
607+ {
608+ "code": "P0003",
609+ "error": "ERROR: P0003: query returned more than one row
610+ HINT: Make sure the query returns a single row, or use LIMIT 1.
611+ CONTEXT: PL/pgSQL function inline_code_block line 6 at SQL statement
612+ ",
613+ "file": "pl_exec.c",
614+ "formattedError": "ERROR: P0003: query returned more than one row
615+ HINT: Make sure the query returns a single row, or use LIMIT 1.
616+ CONTEXT: PL/pgSQL function inline_code_block line 6 at SQL statement
617+ ",
618+ "hint": "Make sure the query returns a single row, or use LIMIT 1.",
619+ "length": 216,
620+ "line": "4349",
621+ "message": "query returned more than one row",
622+ "name": "error",
623+ "routine": "exec_stmt_execsql",
624+ "severity": "ERROR",
625+ "where": "PL/pgSQL function inline_code_block line 6 at SQL statement",
626+ }
627+ ` )
628+ } )
629+
630+ test ( 'error with all formatting properties' , async ( ) => {
631+ // This query will generate an error with all formatting properties
632+ const res = await app . inject ( {
633+ method : 'POST' ,
634+ path : '/query' ,
635+ payload : {
636+ query : `
637+ DO $$
638+ BEGIN
639+ -- Using EXECUTE to force internal query to appear
640+ EXECUTE 'SELECT * FROM nonexistent_table WHERE id = 1';
641+ EXCEPTION WHEN OTHERS THEN
642+ -- Re-raise with added context
643+ RAISE EXCEPTION USING
644+ ERRCODE = SQLSTATE,
645+ MESSAGE = SQLERRM,
646+ DETAIL = 'This is additional detail information',
647+ HINT = 'This is a hint for fixing the issue',
648+ SCHEMA = 'public';
649+ END $$;
650+ ` ,
651+ } ,
652+ } )
653+
654+ expect ( res . json ( ) ) . toMatchInlineSnapshot ( `
655+ {
656+ "code": "42P01",
657+ "detail": "This is additional detail information",
658+ "error": "ERROR: 42P01: relation "nonexistent_table" does not exist
659+ DETAIL: This is additional detail information
660+ HINT: This is a hint for fixing the issue
661+ CONTEXT: PL/pgSQL function inline_code_block line 7 at RAISE
662+ ",
663+ "file": "pl_exec.c",
664+ "formattedError": "ERROR: 42P01: relation "nonexistent_table" does not exist
665+ DETAIL: This is additional detail information
666+ HINT: This is a hint for fixing the issue
667+ CONTEXT: PL/pgSQL function inline_code_block line 7 at RAISE
668+ ",
669+ "hint": "This is a hint for fixing the issue",
670+ "length": 242,
671+ "line": "3859",
672+ "message": "relation "nonexistent_table" does not exist",
673+ "name": "error",
674+ "routine": "exec_stmt_raise",
675+ "schema": "public",
676+ "severity": "ERROR",
677+ "where": "PL/pgSQL function inline_code_block line 7 at RAISE",
678+ }
679+ ` )
680+ } )
681+
682+ test ( 'error with internalQuery property' , async ( ) => {
683+ // First create a function that will execute a query internally
684+ await app . inject ( {
685+ method : 'POST' ,
686+ path : '/query' ,
687+ payload : {
688+ query : `
689+ CREATE OR REPLACE FUNCTION test_internal_query() RETURNS void AS $$
690+ BEGIN
691+ -- This query will be the "internal query" when it fails
692+ EXECUTE 'SELECT * FROM nonexistent_table';
693+ RETURN;
694+ END;
695+ $$ LANGUAGE plpgsql;
696+ ` ,
697+ } ,
698+ } )
699+
700+ // Now call the function to trigger the error with internalQuery
701+ const res = await app . inject ( {
702+ method : 'POST' ,
703+ path : '/query' ,
704+ payload : {
705+ query : 'SELECT test_internal_query();' ,
706+ } ,
707+ } )
708+
709+ expect ( res . json ( ) ) . toMatchInlineSnapshot ( `
710+ {
711+ "code": "42P01",
712+ "error": "ERROR: 42P01: relation "nonexistent_table" does not exist
713+ QUERY: SELECT * FROM nonexistent_table
714+ CONTEXT: PL/pgSQL function test_internal_query() line 4 at EXECUTE
715+ ",
716+ "file": "parse_relation.c",
717+ "formattedError": "ERROR: 42P01: relation "nonexistent_table" does not exist
718+ QUERY: SELECT * FROM nonexistent_table
719+ CONTEXT: PL/pgSQL function test_internal_query() line 4 at EXECUTE
720+ ",
721+ "internalPosition": "15",
722+ "internalQuery": "SELECT * FROM nonexistent_table",
723+ "length": 208,
724+ "line": "1381",
725+ "message": "relation "nonexistent_table" does not exist",
726+ "name": "error",
727+ "routine": "parserOpenTable",
728+ "severity": "ERROR",
729+ "where": "PL/pgSQL function test_internal_query() line 4 at EXECUTE",
730+ }
731+ ` )
732+ } )
0 commit comments