Mercurial > ~dholland > hg > swallowtail > index.cgi
comparison shelltools/query-pr/query.py @ 48:3d5adf5a59d0
Fix python2.7 syntax errors.
author | David A. Holland |
---|---|
date | Sat, 02 Apr 2022 18:05:27 -0400 |
parents | bcd1d06838fd |
children | 4b7f0ee35994 |
comparison
equal
deleted
inserted
replaced
47:bcd1d06838fd | 48:3d5adf5a59d0 |
---|---|
1 #!@PYTHON@ | 1 #!@PYTHON@ |
2 | 2 |
3 import sys | |
3 import argparse | 4 import argparse |
4 import psycopg2 | 5 import psycopg2 |
5 | 6 |
6 program_description = """ | 7 program_description = """ |
7 Search for and retrieve problem reports. | 8 Search for and retrieve problem reports. |
38 "release", "environment" | 39 "release", "environment" |
39 ] | 40 ] |
40 | 41 |
41 # these fields are aliases for others | 42 # these fields are aliases for others |
42 alias_fields = { | 43 alias_fields = { |
43 "number" : "id" | 44 "number" : "id", |
44 "date" : "arrival_date" | 45 "date" : "arrival_date", |
45 } | 46 } |
46 | 47 |
47 def __init__(self): | 48 def __init__(self): |
48 self.present = {} | 49 self.present = {} |
49 self.joined = {} | 50 self.joined = {} |
58 def makepresent(self, field, name): | 59 def makepresent(self, field, name): |
59 self.present[field] = name | 60 self.present[field] = name |
60 return name | 61 return name |
61 | 62 |
62 # add a join item (once only) (internal) | 63 # add a join item (once only) (internal) |
63 def addjoin(self, table, as = None): | 64 def addjoin(self, table, as_ = None): |
64 if as is not None: | 65 if as_ is not None: |
65 key = table + "-" + as | 66 key = table + "-" + as_ |
66 val = table + " AS " + as | 67 val = table + " AS " + as_ |
67 else: | 68 else: |
68 key = table | 69 key = table |
69 val = table | 70 val = table |
70 if key not in self.joined: | 71 if key not in self.joined: |
71 self.joined[key] = True | 72 self.joined[key] = True |
125 self.addwhere("patches.mimetype = " + | 126 self.addwhere("patches.mimetype = " + |
126 "'application/x-patch'") | 127 "'application/x-patch'") |
127 return self.makepresent(field, "patches.body") | 128 return self.makepresent(field, "patches.body") |
128 | 129 |
129 if field == "mimetype": | 130 if field == "mimetype": |
130 subquery = "((SELECT mtmessages1.pr as pr, " + | 131 subquery = "((SELECT mtmessages1.pr as pr, " + \ |
131 "mtmessages1.mimetype as mimetype " + | 132 "mtmessages1.mimetype as mimetype " + \ |
132 "FROM messages as mtmessages1) " + | 133 "FROM messages as mtmessages1) " + \ |
133 "UNION " + | 134 "UNION " + \ |
134 "(SELECT mtmessages2.pr as pr, " + | 135 "(SELECT mtmessages2.pr as pr, " + \ |
135 "mtattach2.mimetype as mimetype " + | 136 "mtattach2.mimetype as mimetype " + \ |
136 "FROM messages as mtmessages2, " + | 137 "FROM messages as mtmessages2, " + \ |
137 " attachments as mtattach2 " + | 138 " attachments as mtattach2 " + \ |
138 "WHERE mtmessages2.id = mtattach2.msgid))" | 139 "WHERE mtmessages2.id = mtattach2.msgid))" |
139 self.addjoin("PRs") | 140 self.addjoin("PRs") |
140 self.addjoin(subquery, "mimetypes") | 141 self.addjoin(subquery, "mimetypes") |
141 self.addwhere("PRs.id = mimetypes.pr") | 142 self.addwhere("PRs.id = mimetypes.pr") |
142 return self.makepresent(field, "mimetypes.mimetype") | 143 return self.makepresent(field, "mimetypes.mimetype") |
260 ############################################################ | 261 ############################################################ |
261 # query class for searches | 262 # query class for searches |
262 # XXX: obsolete, remove | 263 # XXX: obsolete, remove |
263 | 264 |
264 class Query: | 265 class Query: |
265 __init__(self): | 266 def __init__(self): |
266 self.selections = [] | 267 self.selections = [] |
267 self.tables = [] | 268 self.tables = [] |
268 self.constraints = [] | 269 self.constraints = [] |
269 self.args = [] | 270 self.args = [] |
270 prtables = ["PRs"] | 271 prtables = ["PRs"] |
394 regexp_constraint(prq, "PRs.release", args.release) | 395 regexp_constraint(prq, "PRs.release", args.release) |
395 if args.environment is not None: | 396 if args.environment is not None: |
396 checkprtable = True | 397 checkprtable = True |
397 regexp_constraint(prq, "PRs.environment", args.environment) | 398 regexp_constraint(prq, "PRs.environment", args.environment) |
398 | 399 |
399 if args.originator_name is not None or | 400 if args.originator_name is not None or \ |
400 args.originator_email is not None: | 401 args.originator_email is not None: |
401 prq.addtable("usermail as originator") | 402 prq.addtable("usermail as originator") |
402 prq.constrain("PRs.originator = originator.id") | 403 prq.constrain("PRs.originator = originator.id") |
403 if args.originator_name is not None: | 404 if args.originator_name is not None: |
404 checkprtable = True | 405 checkprtable = True |
485 class PrintText: | 486 class PrintText: |
486 def __init__(self, output): | 487 def __init__(self, output): |
487 self.lines = (output == "RAW" or output == "LIST") | 488 self.lines = (output == "RAW" or output == "LIST") |
488 def printheader(self, row): | 489 def printheader(self, row): |
489 # nothing | 490 # nothing |
491 pass | |
490 def printrow(self, row): | 492 def printrow(self, row): |
491 # XXX | 493 # XXX |
492 print row | 494 print row |
493 def printfooter(self, row): | 495 def printfooter(self, row): |
494 # nothing | 496 # nothing |
497 pass | |
495 # end class PrintText | 498 # end class PrintText |
496 | 499 |
497 class PrintCsv: | 500 class PrintCsv: |
498 def __init__(self, output): | 501 def __init__(self, output): |
499 # nothing | 502 # nothing |
503 pass | |
500 def printheader(self, row): | 504 def printheader(self, row): |
501 # XXX | 505 # XXX |
506 pass | |
502 def printrow(self, row): | 507 def printrow(self, row): |
503 # XXX | 508 # XXX |
509 pass | |
504 def printfooter(self, row): | 510 def printfooter(self, row): |
505 # nothing | 511 # nothing |
512 pass | |
506 # end class PrintCsv | 513 # end class PrintCsv |
507 | 514 |
508 class PrintXml: | 515 class PrintXml: |
509 def __init__(self, output): | 516 def __init__(self, output): |
510 # nothing | 517 # nothing |
518 pass | |
511 def printheader(self, row): | 519 def printheader(self, row): |
512 # XXX | 520 # XXX |
521 pass | |
513 def printrow(self, row): | 522 def printrow(self, row): |
514 # XXX | 523 # XXX |
524 pass | |
515 def printfooter(self, row): | 525 def printfooter(self, row): |
516 # XXX | 526 # XXX |
527 pass | |
517 # end class PrintXml | 528 # end class PrintXml |
518 | 529 |
519 class PrintJson: | 530 class PrintJson: |
520 def __init__(self, output): | 531 def __init__(self, output): |
521 # nothing | 532 # nothing |
533 pass | |
522 def printheader(self, row): | 534 def printheader(self, row): |
523 # XXX | 535 # XXX |
536 pass | |
524 def printrow(self, row): | 537 def printrow(self, row): |
525 # XXX | 538 # XXX |
539 pass | |
526 def printfooter(self, row): | 540 def printfooter(self, row): |
527 # XXX | 541 # XXX |
542 pass | |
528 # end class PrintJson | 543 # end class PrintJson |
529 | 544 |
530 class PrintRdf: | 545 class PrintRdf: |
531 def __init__(self, output): | 546 def __init__(self, output): |
532 # nothing | 547 # nothing |
548 pass | |
533 def printheader(self, row): | 549 def printheader(self, row): |
534 # XXX | 550 # XXX |
551 pass | |
535 def printrow(self, row): | 552 def printrow(self, row): |
536 # XXX | 553 # XXX |
554 pass | |
537 def printfooter(self, row): | 555 def printfooter(self, row): |
538 # XXX | 556 # XXX |
557 pass | |
539 # end class PrintRdf | 558 # end class PrintRdf |
540 | 559 |
541 class PrintRdflike: | 560 class PrintRdflike: |
542 def __init__(self, output): | 561 def __init__(self, output): |
543 # nothing | 562 # nothing |
563 pass | |
544 def printheader(self, row): | 564 def printheader(self, row): |
545 # XXX | 565 # XXX |
566 pass | |
546 def printrow(self, row): | 567 def printrow(self, row): |
547 # XXX | 568 # XXX |
569 pass | |
548 def printfooter(self, row): | 570 def printfooter(self, row): |
549 # XXX | 571 # XXX |
572 pass | |
550 # end class PrintRdflike | 573 # end class PrintRdflike |
551 | 574 |
552 def print_prs(ids): | 575 def print_prs(ids): |
553 if sel.outformat == "TEXT": | 576 if sel.outformat == "TEXT": |
554 mkprinter = PrintText | 577 mkprinter = PrintText |
575 printer.printfooter(ids[0]) | 598 printer.printfooter(ids[0]) |
576 return | 599 return |
577 elif sel.output == "LIST": | 600 elif sel.output == "LIST": |
578 # XXX is there a clean way to do this passing the | 601 # XXX is there a clean way to do this passing the |
579 # whole list of ids at once? | 602 # whole list of ids at once? |
580 query = "SELECT id, synopsis\n" + | 603 query = "SELECT id, synopsis\n" + \ |
581 "FROM PRs\n" + | 604 "FROM PRs\n" + \ |
582 "WHERE id = $1" | 605 "WHERE id = $1" |
583 elif sel.output == "HEADERS": | 606 elif sel.output == "HEADERS": |
584 query = None # XXX | 607 query = None # XXX |
585 elif sel.output == "META": | 608 elif sel.output == "META": |
586 query = None # XXX | 609 query = None # XXX |
600 printer.printfooter(results[0]) | 623 printer.printfooter(results[0]) |
601 # end print_prs | 624 # end print_prs |
602 | 625 |
603 # XXX if in public mode we need to check if the PR is public | 626 # XXX if in public mode we need to check if the PR is public |
604 def print_message(pr, msgnum): | 627 def print_message(pr, msgnum): |
605 query = "SELECT users.username AS username,\n" + | 628 query = "SELECT users.username AS username,\n" + \ |
606 " users.realname AS realname,\n" + | 629 " users.realname AS realname,\n" + \ |
607 " messages.id AS id, parent_id,\n" + | 630 " messages.id AS id, parent_id,\n" + \ |
608 " posttime, mimetype, body\n" + | 631 " posttime, mimetype, body\n" + \ |
609 "FROM messages, users\n" + | 632 "FROM messages, users\n" + \ |
610 "WHERE messages.who = users.id\n" + | 633 "WHERE messages.who = users.id\n" + \ |
611 " AND messages.pr = $1\n" + | 634 " AND messages.pr = $1\n" + \ |
612 " AND messages.number_in_pr = $2\n" | 635 " AND messages.number_in_pr = $2\n" |
613 # Note that while pr is safe, msgnum came from the commandline | 636 # Note that while pr is safe, msgnum came from the commandline |
614 # and may not be. | 637 # and may not be. |
615 results = querydb(query, [pr, msgnum]) | 638 results = querydb(query, [pr, msgnum]) |
616 [result] = results | 639 [result] = results |
626 sys.stdout.write(body) | 649 sys.stdout.write(body) |
627 # end print_message | 650 # end print_message |
628 | 651 |
629 # XXX if in public mode we need to check if the PR is public | 652 # XXX if in public mode we need to check if the PR is public |
630 def print_attachment(pr, attachnum): | 653 def print_attachment(pr, attachnum): |
631 query = "SELECT a.mimetype as mimetype, a.body as body\n" + | 654 query = "SELECT a.mimetype as mimetype, a.body as body\n" + \ |
632 "FROM messages, attachments as a\n" + | 655 "FROM messages, attachments as a\n" + \ |
633 "WHERE messages.pr = $1\n" + | 656 "WHERE messages.pr = $1\n" + \ |
634 " AND messages.id = a.msgid\n" + | 657 " AND messages.id = a.msgid\n" + \ |
635 " AND a.number_in_pr = $2\n" | 658 " AND a.number_in_pr = $2\n" |
636 # Note that while pr is safe, attachnum came from the | 659 # Note that while pr is safe, attachnum came from the |
637 # commandline and may not be. | 660 # commandline and may not be. |
638 results = querydb(query, [pr, msgnum]) | 661 results = querydb(query, [pr, msgnum]) |
639 [result] = results | 662 [result] = results |
700 def dorevfield(field): | 723 def dorevfield(field): |
701 return Order(field, True) | 724 return Order(field, True) |
702 # end class Order | 725 # end class Order |
703 | 726 |
704 class Search: | 727 class Search: |
705 def __init__(self, qs, openonly, publiconly, os) | 728 def __init__(self, qs, openonly, publiconly, os): |
706 self.queries = qs | 729 self.queries = qs |
707 self.openonly = openonly | 730 self.openonly = openonly |
708 self.publiconly = publiconly | 731 self.publiconly = publiconly |
709 self.orders = os | 732 self.orders = os |
710 # end class Search | 733 # end class Search |
763 # end class Invocation | 786 # end class Invocation |
764 | 787 |
765 ############################################################ | 788 ############################################################ |
766 # run (eval the SQL and print the results) | 789 # run (eval the SQL and print the results) |
767 | 790 |
768 def | |
769 | |
770 def run_sel(sel, ids): | 791 def run_sel(sel, ids): |
771 if sel.type == S_PR: | 792 if sel.type == S_PR: |
772 if ids == []: | 793 if ids == []: |
773 sys.stderr.write("No PRs matched.\n") | 794 sys.stderr.write("No PRs matched.\n") |
774 exit(1) | 795 exit(1) |
815 # | 836 # |
816 # XXX this doesn't work, we need to keep the interned strings | 837 # XXX this doesn't work, we need to keep the interned strings |
817 # on return from compile_query. | 838 # on return from compile_query. |
818 # | 839 # |
819 | 840 |
841 def matches(s, rx): | |
842 # XXX | |
843 return True | |
844 | |
820 def compile_query(q): | 845 def compile_query(q): |
821 if q.type == Q_QSTRING: | 846 if q.type == Q_QSTRING: |
822 # XXX should use a split that honors quotes | 847 # XXX should use a split that honors quotes |
823 terms = q.string.split() | 848 terms = q.string.split() |
824 terms = [dotstring(t) for t in terms] | 849 terms = [dotstring(t) for t in terms] |
825 return compile_query(doand(terms)) | 850 return compile_query(doand(terms)) |
826 if q.type == Q_TSTRING: | 851 if q.type == Q_TSTRING: |
827 qb = QueryBuilder() | 852 qb = QueryBuilder() |
828 s = q.string | 853 s = q.string |
829 if s ~ "^[0-9]+$": | 854 if matches(s, "^[0-9]+$"): |
830 f = qb.getfield("number") | 855 f = qb.getfield("number") |
831 # Note: s is user-supplied but clean to insert directly | 856 # Note: s is user-supplied but clean to insert directly |
832 qb.addwhere("%s = %s" % (f, s)) | 857 qb.addwhere("%s = %s" % (f, s)) |
833 elif s ~ "^[0-9]+-[0-9]+$": | 858 elif matches(s, "^[0-9]+-[0-9]+$"): |
834 f = qb.getfield("number") | 859 f = qb.getfield("number") |
835 ss = s.split("-") | 860 ss = s.split("-") |
836 # Note: ss[] is user-supplied but clean | 861 # Note: ss[] is user-supplied but clean |
837 qb.addwhere("%s >= %s" % (f, ss[0])) | 862 qb.addwhere("%s >= %s" % (f, ss[0])) |
838 qb.addwhere("%s <= %s" % (f, ss[1])) | 863 qb.addwhere("%s <= %s" % (f, ss[1])) |
839 elif s ~ "^[0-9]+-$": | 864 elif matches(s, "^[0-9]+-$"): |
840 f = qb.getfield("number") | 865 f = qb.getfield("number") |
841 ss = s.split("-") | 866 ss = s.split("-") |
842 # Note: ss[] is user-supplied but clean | 867 # Note: ss[] is user-supplied but clean |
843 qb.addwhere("%s >= %s" % (f, ss[0])) | 868 qb.addwhere("%s >= %s" % (f, ss[0])) |
844 elif s ~ "^-[0-9]+$": | 869 elif matches(s, "^-[0-9]+$"): |
845 f = qb.getfield("number") | 870 f = qb.getfield("number") |
846 ss = s.split("-") | 871 ss = s.split("-") |
847 # Note: ss[] is user-supplied but clean | 872 # Note: ss[] is user-supplied but clean |
848 qb.addwhere("%s <= %s" % (f, ss[1])) | 873 qb.addwhere("%s <= %s" % (f, ss[1])) |
849 elif s ~ "^[^:]+:[^:]+$": | 874 elif matches(s, "^[^:]+:[^:]+$"): |
850 # XXX honor quoted terms | 875 # XXX honor quoted terms |
851 # XXX = or LIKE? | 876 # XXX = or LIKE? |
852 ss = s.split(":") | 877 ss = s.split(":") |
853 # ss[0] is not clean but if it's crap it won't match | 878 # ss[0] is not clean but if it's crap it won't match |
854 f = qb.getfield(ss[0]) | 879 f = qb.getfield(ss[0]) |
855 # ss[1] is not clean, so intern it for safety | 880 # ss[1] is not clean, so intern it for safety |
856 s = qb.intern(ss[1]) | 881 s = qb.intern(ss[1]) |
857 qb.addwhere("%s = %s" % (f, s)) | 882 qb.addwhere("%s = %s" % (f, s)) |
858 elif s ~ "^-[^:]+:[^:]+$" | 883 elif matches(s, "^-[^:]+:[^:]+$"): |
859 # XXX honor quoted terms | 884 # XXX honor quoted terms |
860 # XXX <> or NOT LIKE? | 885 # XXX <> or NOT LIKE? |
861 ss = s.split(":") | 886 ss = s.split(":") |
862 # ss[0] is not clean but if it's crap it won't match | 887 # ss[0] is not clean but if it's crap it won't match |
863 f = qb.getfield(ss[0]) | 888 f = qb.getfield(ss[0]) |
864 # ss[1] is not clean, so intern it for safety | 889 # ss[1] is not clean, so intern it for safety |
865 s = qb.intern(ss[1]) | 890 s = qb.intern(ss[1]) |
866 qb.addwhere("%s <> %s" % (f, s)) | 891 qb.addwhere("%s <> %s" % (f, s)) |
867 elif s ~ "^-": | 892 elif matches(s, "^-"): |
868 # XXX <> or NOT LIKE? | 893 # XXX <> or NOT LIKE? |
869 f = qb.getfield("alltext") | 894 f = qb.getfield("alltext") |
870 # s is not clean, so intern it for safety | 895 # s is not clean, so intern it for safety |
871 s = qb.intern(s) | 896 s = qb.intern(s) |
872 qb.addwhere("%s <> %s" % (f, s)) | 897 qb.addwhere("%s <> %s" % (f, s)) |
976 action='version', version=program_version, | 1001 action='version', version=program_version, |
977 help="Print program version and exit") | 1002 help="Print program version and exit") |
978 | 1003 |
979 p.add_argument("--show", nargs=1, | 1004 p.add_argument("--show", nargs=1, |
980 action=CtorAppend, dest='ops', | 1005 action=CtorAppend, dest='ops', |
981 const=Invocation.Op.doshow | 1006 const=Invocation.Op.doshow, |
982 help="Show description of field") | 1007 help="Show description of field") |
983 p.add_argument("--range", nargs=1, | 1008 p.add_argument("--range", nargs=1, |
984 action=CtorAppend, dest='ops', | 1009 action=CtorAppend, dest='ops', |
985 const=Invocation.Op.dorange | 1010 const=Invocation.Op.dorange, |
986 help="Show range of extant values for field") | 1011 help="Show range of extant values for field") |
987 | 1012 |
988 p.add_argument("--search", nargs=1, | 1013 p.add_argument("--search", nargs=1, |
989 action=CtorAppend, dest='queries', | 1014 action=CtorAppend, dest='queries', |
990 const=Invocation.Query.doqstring, | 1015 const=Invocation.Query.doqstring, |