|
23 | 23 | from django.utils.encoding import smart_str |
24 | 24 | from django.contrib.contenttypes.models import ContentType |
25 | 25 |
|
| 26 | +# psycopg |
| 27 | +from psycopg import sql |
| 28 | + |
26 | 29 | # REST Framework |
27 | 30 | from rest_framework.exceptions import ParseError |
28 | 31 |
|
@@ -1141,17 +1144,23 @@ def result_stdout_raw_handle(self, enforce_max_bytes=True): |
1141 | 1144 | raise StdoutMaxBytesExceeded(total, max_supported) |
1142 | 1145 |
|
1143 | 1146 | tbl = self._meta.db_table + 'event' |
1144 | | - created_by_cond = '' |
| 1147 | + where_parts = [ |
| 1148 | + sql.SQL('{} = {}').format(sql.Identifier(self.event_parent_key), sql.Literal(self.id)), |
| 1149 | + sql.SQL("stdout != ''"), |
| 1150 | + ] |
1145 | 1151 | if self.has_unpartitioned_events: |
1146 | | - tbl = f'_unpartitioned_{tbl}' |
| 1152 | + tbl = '_unpartitioned_' + tbl |
1147 | 1153 | else: |
1148 | | - created_by_cond = f"job_created='{self.created.isoformat()}' AND " |
| 1154 | + where_parts.insert(0, sql.SQL('job_created = {}').format(sql.Literal(self.created))) |
1149 | 1155 |
|
1150 | | - sql = f"copy (select stdout from {tbl} where {created_by_cond}{self.event_parent_key}={self.id} and stdout != '' order by start_line) to stdout" # nosql |
| 1156 | + copy_sql = sql.SQL('COPY (SELECT stdout FROM {} WHERE {} ORDER BY start_line) TO STDOUT').format( |
| 1157 | + sql.Identifier(tbl), |
| 1158 | + sql.SQL(' AND ').join(where_parts), |
| 1159 | + ) |
1151 | 1160 | # psycopg3's copy writes bytes, but callers of this |
1152 | 1161 | # function assume a str-based fd will be returned; decode |
1153 | 1162 | # .write() calls on the fly to maintain this interface |
1154 | | - with cursor.copy(sql) as copy: |
| 1163 | + with cursor.copy(copy_sql) as copy: |
1155 | 1164 | while data := copy.read(): |
1156 | 1165 | fd.write(smart_str(bytes(data))) |
1157 | 1166 |
|
|
0 commit comments