harlequin-postgres 1.2.0__tar.gz → 1.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: harlequin-postgres
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: A Harlequin adapter for Postgres.
5
5
  License: MIT
6
6
  Author: Ted Conbeer
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "harlequin-postgres"
3
- version = "1.2.0"
3
+ version = "1.2.2"
4
4
  description = "A Harlequin adapter for Postgres."
5
5
  authors = ["Ted Conbeer <tconbeer@users.noreply.github.com>"]
6
6
  license = "MIT"
@@ -136,10 +136,19 @@ class HarlequinPostgresConnection(HarlequinConnection):
136
136
  cur.close()
137
137
  return None
138
138
  except Exception as e:
139
- cur.close()
140
- self.rollback()
139
+ msg_suffix = ""
140
+ try:
141
+ cur.close()
142
+ self.rollback()
143
+ except Exception:
144
+ # likely connection is closed; error messages
145
+ # can be cryptic, so help the user.
146
+ msg_suffix = (
147
+ "\n\nYou may need to restart Harlequin to reconnect to the "
148
+ "database."
149
+ )
141
150
  raise HarlequinQueryError(
142
- msg=str(e),
151
+ msg=f"{e}{msg_suffix}",
143
152
  title="Harlequin encountered an error while executing your query.",
144
153
  ) from e
145
154
  else:
@@ -217,15 +226,16 @@ class HarlequinPostgresConnection(HarlequinConnection):
217
226
  conn: Connection = self.pool.getconn()
218
227
  with conn.cursor() as cur:
219
228
  cur.execute(
220
- f"""
229
+ """
221
230
  select schema_name
222
231
  from information_schema.schemata
223
232
  where
224
- catalog_name = '{dbname}'
233
+ catalog_name = %s
225
234
  and schema_name != 'information_schema'
226
- and schema_name not like 'pg_%'
235
+ and schema_name not like 'pg_%%'
227
236
  order by schema_name asc
228
- ;"""
237
+ ;""",
238
+ (dbname,),
229
239
  )
230
240
  results: list[tuple[str]] = cur.fetchall()
231
241
  self.pool.putconn(conn)
@@ -235,34 +245,78 @@ class HarlequinPostgresConnection(HarlequinConnection):
235
245
  conn: Connection = self.pool.getconn()
236
246
  with conn.cursor() as cur:
237
247
  cur.execute(
238
- f"""
248
+ """
239
249
  select table_name, table_type
240
250
  from information_schema.tables
241
251
  where
242
- table_catalog = '{dbname}'
243
- and table_schema = '{schema}'
252
+ table_catalog = %s
253
+ and table_schema = %s
244
254
  order by table_name asc
245
- ;"""
255
+ ;""",
256
+ (dbname, schema),
246
257
  )
247
258
  results: list[tuple[str, str]] = cur.fetchall()
248
259
  self.pool.putconn(conn)
249
260
  return results
250
261
 
262
+ # only works for the currently-connected db
263
+ def _get_mvs(self, schema: str) -> list[tuple[str]]:
264
+ conn: Connection = self.pool.getconn()
265
+ with conn.cursor() as cur:
266
+ cur.execute(
267
+ """
268
+ select matviewname
269
+ from pg_matviews
270
+ where
271
+ schemaname = %s
272
+ order by matviewname asc
273
+ ;""",
274
+ (schema,),
275
+ )
276
+ results: list[tuple[str]] = cur.fetchall()
277
+ self.pool.putconn(conn)
278
+ return results
279
+
251
280
  def _get_columns(
252
281
  self, dbname: str, schema: str, relation: str
253
282
  ) -> list[tuple[str, str]]:
254
283
  conn: Connection = self.pool.getconn()
255
284
  with conn.cursor() as cur:
256
285
  cur.execute(
257
- f"""
286
+ """
258
287
  select column_name, data_type
259
288
  from information_schema.columns
260
289
  where
261
- table_catalog = '{dbname}'
262
- and table_schema = '{schema}'
263
- and table_name = '{relation}'
290
+ table_catalog = %s
291
+ and table_schema = %s
292
+ and table_name = %s
264
293
  order by ordinal_position asc
265
- ;"""
294
+ ;""",
295
+ (dbname, schema, relation),
296
+ )
297
+ results: list[tuple[str, str]] = cur.fetchall()
298
+ self.pool.putconn(conn)
299
+ return results
300
+
301
+ def _get_mv_cols(self, schema: str, mv: str) -> list[tuple[str, str]]:
302
+ conn: Connection = self.pool.getconn()
303
+ with conn.cursor() as cur:
304
+ cur.execute(
305
+ """
306
+ select
307
+ a.attname,
308
+ pg_catalog.format_type(a.atttypid, a.atttypmod)
309
+ from pg_attribute a
310
+ join pg_class t on a.attrelid = t.oid
311
+ join pg_namespace s on t.relnamespace = s.oid
312
+ where
313
+ a.attnum > 0
314
+ and not a.attisdropped
315
+ and s.nspname = %s
316
+ and t.relname = %s
317
+ order by a.attnum;
318
+ ;""",
319
+ (schema, mv),
266
320
  )
267
321
  results: list[tuple[str, str]] = cur.fetchall()
268
322
  self.pool.putconn(conn)
@@ -166,6 +166,38 @@ class ForeignCatalogItem(TableCatalogItem):
166
166
  )
167
167
 
168
168
 
169
+ class MaterializedViewCatalogItem(RelationCatalogItem):
170
+ @classmethod
171
+ def from_parent(
172
+ cls,
173
+ parent: "SchemaCatalogItem",
174
+ label: str,
175
+ ) -> "MaterializedViewCatalogItem":
176
+ relation_query_name = f'"{parent.label}"."{label}"'
177
+ relation_qualified_identifier = f'{parent.qualified_identifier}."{label}"'
178
+ return cls(
179
+ qualified_identifier=relation_qualified_identifier,
180
+ query_name=relation_query_name,
181
+ label=label,
182
+ type_label="mv",
183
+ connection=parent.connection,
184
+ parent=parent,
185
+ )
186
+
187
+ def fetch_children(self) -> list[ColumnCatalogItem]:
188
+ if self.parent is None or self.parent.parent is None or self.connection is None:
189
+ return []
190
+ result = self.connection._get_mv_cols(self.parent.label, self.label)
191
+ return [
192
+ ColumnCatalogItem.from_parent(
193
+ parent=self,
194
+ label=column_name,
195
+ type_label=self.connection._short_column_type(column_type),
196
+ )
197
+ for column_name, column_type in result
198
+ ]
199
+
200
+
169
201
  @dataclass
170
202
  class SchemaCatalogItem(InteractiveCatalogItem["HarlequinPostgresConnection"]):
171
203
  INTERACTIONS = [
@@ -227,6 +259,14 @@ class SchemaCatalogItem(InteractiveCatalogItem["HarlequinPostgresConnection"]):
227
259
  )
228
260
  )
229
261
 
262
+ for (mv_label,) in self.connection._get_mvs(self.label):
263
+ children.append(
264
+ MaterializedViewCatalogItem.from_parent(
265
+ parent=self,
266
+ label=mv_label,
267
+ )
268
+ )
269
+
230
270
  return children
231
271
 
232
272