Skip to content

Commit 13ca7d2

Browse files
ERYoung11j-bennet
andauthored
fixed #1403, improved comment handling (#1404)
* fixed #1403, improved comment handling * black + hooks + changelog --------- Co-authored-by: Irina Truong <[email protected]>
1 parent 6332e18 commit 13ca7d2

File tree

3 files changed

+179
-20
lines changed

3 files changed

+179
-20
lines changed

changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Bug fixes:
3131
* Allow specifying an `alias_map_file` in the config that will use
3232
predetermined table aliases instead of generating aliases programmatically on
3333
the fly
34+
* Fixed SQL error when there is a comment on the first line: ([issue 1403](https://github.com/dbcli/pgcli/issues/1403))
3435

3536
Internal:
3637
---------

pgcli/pgexecute.py

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
import traceback
33
from collections import namedtuple
4-
4+
import re
55
import pgspecial as special
66
import psycopg
77
import psycopg.sql
@@ -17,6 +17,27 @@
1717
)
1818

1919

20+
# we added this funcion to strip beginning comments
21+
# because sqlparse didn't handle tem well. It won't be needed if sqlparse
22+
# does parsing of this situation better
23+
24+
25+
def remove_beginning_comments(command):
26+
# Regular expression pattern to match comments
27+
pattern = r"^(/\*.*?\*/|--.*?)(?:\n|$)"
28+
29+
# Find and remove all comments from the beginning
30+
cleaned_command = command
31+
comments = []
32+
match = re.match(pattern, cleaned_command, re.DOTALL)
33+
while match:
34+
comments.append(match.group())
35+
cleaned_command = cleaned_command[len(match.group()) :].lstrip()
36+
match = re.match(pattern, cleaned_command, re.DOTALL)
37+
38+
return [cleaned_command, comments]
39+
40+
2041
def register_typecasters(connection):
2142
"""Casts date and timestamp values to string, resolves issues with out-of-range
2243
dates (e.g. BC) which psycopg can't handle"""
@@ -311,21 +332,20 @@ def run(
311332
# sql parse doesn't split on a comment first + special
312333
# so we're going to do it
313334

314-
sqltemp = []
335+
removed_comments = []
315336
sqlarr = []
337+
cleaned_command = ""
316338

317-
if statement.startswith("--"):
318-
sqltemp = statement.split("\n")
319-
sqlarr.append(sqltemp[0])
320-
for i in sqlparse.split(sqltemp[1]):
321-
sqlarr.append(i)
322-
elif statement.startswith("/*"):
323-
sqltemp = statement.split("*/")
324-
sqltemp[0] = sqltemp[0] + "*/"
325-
for i in sqlparse.split(sqltemp[1]):
326-
sqlarr.append(i)
327-
else:
328-
sqlarr = sqlparse.split(statement)
339+
# could skip if statement doesn't match ^-- or ^/*
340+
cleaned_command, removed_comments = remove_beginning_comments(statement)
341+
342+
sqlarr = sqlparse.split(cleaned_command)
343+
344+
# now re-add the beginning comments if there are any, so that they show up in
345+
# log files etc when running these commands
346+
347+
if len(removed_comments) > 0:
348+
sqlarr = removed_comments + sqlarr
329349

330350
# run each sql query
331351
for sql in sqlarr:

tests/test_pgexecute.py

Lines changed: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,7 @@ def test_execute_from_commented_file_that_executes_another_file(
304304

305305
@dbtest
306306
def test_execute_commented_first_line_and_special(executor, pgspecial, tmpdir):
307-
# https://github.com/dbcli/pgcli/issues/1362
308-
309-
# just some base caes that should work also
307+
# just some base cases that should work also
310308
statement = "--comment\nselect now();"
311309
result = run(executor, statement, pgspecial=pgspecial)
312310
assert result != None
@@ -317,12 +315,14 @@ def test_execute_commented_first_line_and_special(executor, pgspecial, tmpdir):
317315
assert result != None
318316
assert result[1].find("now") >= 0
319317

320-
statement = "/*comment\ncomment line2*/\nselect now();"
318+
# https://github.com/dbcli/pgcli/issues/1362
319+
statement = "--comment\n\\h"
321320
result = run(executor, statement, pgspecial=pgspecial)
322321
assert result != None
323-
assert result[1].find("now") >= 0
322+
assert result[1].find("ALTER") >= 0
323+
assert result[1].find("ABORT") >= 0
324324

325-
statement = "--comment\n\\h"
325+
statement = "--comment1\n--comment2\n\\h"
326326
result = run(executor, statement, pgspecial=pgspecial)
327327
assert result != None
328328
assert result[1].find("ALTER") >= 0
@@ -334,6 +334,24 @@ def test_execute_commented_first_line_and_special(executor, pgspecial, tmpdir):
334334
assert result[1].find("ALTER") >= 0
335335
assert result[1].find("ABORT") >= 0
336336

337+
statement = """/*comment1
338+
comment2*/
339+
\h"""
340+
result = run(executor, statement, pgspecial=pgspecial)
341+
assert result != None
342+
assert result[1].find("ALTER") >= 0
343+
assert result[1].find("ABORT") >= 0
344+
345+
statement = """/*comment1
346+
comment2*/
347+
/*comment 3
348+
comment4*/
349+
\\h"""
350+
result = run(executor, statement, pgspecial=pgspecial)
351+
assert result != None
352+
assert result[1].find("ALTER") >= 0
353+
assert result[1].find("ABORT") >= 0
354+
337355
statement = " /*comment*/\n\h;"
338356
result = run(executor, statement, pgspecial=pgspecial)
339357
assert result != None
@@ -352,6 +370,126 @@ def test_execute_commented_first_line_and_special(executor, pgspecial, tmpdir):
352370
assert result[1].find("ALTER") >= 0
353371
assert result[1].find("ABORT") >= 0
354372

373+
statement = """\\h /*comment4 */"""
374+
result = run(executor, statement, pgspecial=pgspecial)
375+
print(result)
376+
assert result != None
377+
assert result[0].find("No help") >= 0
378+
379+
# TODO: we probably don't want to do this but sqlparse is not parsing things well
380+
# we relly want it to find help but right now, sqlparse isn't dropping the /*comment*/
381+
# style comments after command
382+
383+
statement = """/*comment1*/
384+
\h
385+
/*comment4 */"""
386+
result = run(executor, statement, pgspecial=pgspecial)
387+
assert result != None
388+
assert result[0].find("No help") >= 0
389+
390+
# TODO: same for this one
391+
statement = """/*comment1
392+
comment3
393+
comment2*/
394+
\\h
395+
/*comment4
396+
comment5
397+
comment6*/"""
398+
result = run(executor, statement, pgspecial=pgspecial)
399+
assert result != None
400+
assert result[0].find("No help") >= 0
401+
402+
403+
@dbtest
404+
def test_execute_commented_first_line_and_normal(executor, pgspecial, tmpdir):
405+
# https://github.com/dbcli/pgcli/issues/1403
406+
407+
# just some base cases that should work also
408+
statement = "--comment\nselect now();"
409+
result = run(executor, statement, pgspecial=pgspecial)
410+
assert result != None
411+
assert result[1].find("now") >= 0
412+
413+
statement = "/*comment*/\nselect now();"
414+
result = run(executor, statement, pgspecial=pgspecial)
415+
assert result != None
416+
assert result[1].find("now") >= 0
417+
418+
# this simulates the original error (1403) without having to add/drop tables
419+
# since it was just an error on reading input files and not the actual
420+
# command itself
421+
422+
# test that the statement works
423+
statement = """VALUES (1, 'one'), (2, 'two'), (3, 'three');"""
424+
result = run(executor, statement, pgspecial=pgspecial)
425+
assert result != None
426+
assert result[5].find("three") >= 0
427+
428+
# test the statement with a \n in the middle
429+
statement = """VALUES (1, 'one'),\n (2, 'two'), (3, 'three');"""
430+
result = run(executor, statement, pgspecial=pgspecial)
431+
assert result != None
432+
assert result[5].find("three") >= 0
433+
434+
# test the statement with a newline in the middle
435+
statement = """VALUES (1, 'one'),
436+
(2, 'two'), (3, 'three');"""
437+
result = run(executor, statement, pgspecial=pgspecial)
438+
assert result != None
439+
assert result[5].find("three") >= 0
440+
441+
# now add a single comment line
442+
statement = """--comment\nVALUES (1, 'one'), (2, 'two'), (3, 'three');"""
443+
result = run(executor, statement, pgspecial=pgspecial)
444+
assert result != None
445+
assert result[5].find("three") >= 0
446+
447+
# doing without special char \n
448+
statement = """--comment
449+
VALUES (1,'one'),
450+
(2, 'two'), (3, 'three');"""
451+
result = run(executor, statement, pgspecial=pgspecial)
452+
assert result != None
453+
assert result[5].find("three") >= 0
454+
455+
# two comment lines
456+
statement = """--comment\n--comment2\nVALUES (1,'one'), (2, 'two'), (3, 'three');"""
457+
result = run(executor, statement, pgspecial=pgspecial)
458+
assert result != None
459+
assert result[5].find("three") >= 0
460+
461+
# doing without special char \n
462+
statement = """--comment
463+
--comment2
464+
VALUES (1,'one'), (2, 'two'), (3, 'three');
465+
"""
466+
result = run(executor, statement, pgspecial=pgspecial)
467+
assert result != None
468+
assert result[5].find("three") >= 0
469+
470+
# multiline comment + newline in middle of the statement
471+
statement = """/*comment
472+
comment2
473+
comment3*/
474+
VALUES (1,'one'),
475+
(2, 'two'), (3, 'three');"""
476+
result = run(executor, statement, pgspecial=pgspecial)
477+
assert result != None
478+
assert result[5].find("three") >= 0
479+
480+
# multiline comment + newline in middle of the statement
481+
# + comments after the statement
482+
statement = """/*comment
483+
comment2
484+
comment3*/
485+
VALUES (1,'one'),
486+
(2, 'two'), (3, 'three');
487+
--comment4
488+
--comment5"""
489+
result = run(executor, statement, pgspecial=pgspecial)
490+
assert result != None
491+
assert result[5].find("three") >= 0
492+
355493

356494
@dbtest
357495
def test_multiple_queries_same_line(executor):

0 commit comments

Comments
 (0)