|
17 | 17 | union, |
18 | 18 | ) |
19 | 19 | from sqlalchemy.dialects.postgresql import ARRAY, CHAR, REGCLASS, aggregate_order_by |
| 20 | +from sqlalchemy.sql.functions import coalesce |
20 | 21 |
|
21 | 22 | from sqlalchemy_declarative_extensions.sqlalchemy import select |
22 | 23 |
|
@@ -317,65 +318,90 @@ def get_types(arg_type_oids): |
317 | 318 | ) |
318 | 319 |
|
319 | 320 |
|
320 | | -procedures_query = ( |
321 | | - select( |
322 | | - pg_proc.c.proname.label("name"), |
323 | | - pg_namespace.c.nspname.label("schema"), |
324 | | - pg_language.c.lanname.label("language"), |
325 | | - pg_type.c.typname.label("return_type"), |
326 | | - pg_proc.c.prosrc.label("source"), |
327 | | - pg_proc.c.prosecdef.label("security_definer"), |
328 | | - pg_proc.c.prokind.label("kind"), |
329 | | - pg_proc.c.proargnames.label("arg_names"), |
330 | | - pg_proc.c.proargmodes.label("arg_modes"), |
331 | | - func.coalesce( |
332 | | - get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) |
333 | | - ).label("arg_types"), |
334 | | - func.pg_get_expr( |
335 | | - pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) |
336 | | - ).label("arg_defaults"), |
337 | | - ) |
338 | | - .select_from( |
339 | | - pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) |
340 | | - .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) |
341 | | - .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) |
| 321 | +def get_procedures_query(version_info): |
| 322 | + source = get_source_column(version_info) |
| 323 | + return ( |
| 324 | + select( |
| 325 | + pg_proc.c.proname.label("name"), |
| 326 | + pg_namespace.c.nspname.label("schema"), |
| 327 | + pg_language.c.lanname.label("language"), |
| 328 | + pg_type.c.typname.label("return_type"), |
| 329 | + source.label("source"), |
| 330 | + pg_proc.c.prosecdef.label("security_definer"), |
| 331 | + pg_proc.c.prokind.label("kind"), |
| 332 | + pg_proc.c.proargnames.label("arg_names"), |
| 333 | + pg_proc.c.proargmodes.label("arg_modes"), |
| 334 | + func.coalesce( |
| 335 | + get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) |
| 336 | + ).label("arg_types"), |
| 337 | + func.pg_get_expr( |
| 338 | + pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) |
| 339 | + ).label("arg_defaults"), |
| 340 | + ) |
| 341 | + .select_from( |
| 342 | + pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) |
| 343 | + .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) |
| 344 | + .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) |
| 345 | + ) |
| 346 | + .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) |
| 347 | + .where(pg_proc.c.prokind == "p") |
| 348 | + .where(_schema_not_from_extension()) |
| 349 | + .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) |
342 | 350 | ) |
343 | | - .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) |
344 | | - .where(pg_proc.c.prokind == "p") |
345 | | - .where(_schema_not_from_extension()) |
346 | | - .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) |
347 | | -) |
348 | 351 |
|
349 | | -functions_query = ( |
350 | | - select( |
351 | | - pg_proc.c.proname.label("name"), |
352 | | - pg_namespace.c.nspname.label("schema"), |
353 | | - pg_language.c.lanname.label("language"), |
354 | | - pg_type.c.typname.label("base_return_type"), |
355 | | - pg_proc.c.prosrc.label("source"), |
356 | | - pg_proc.c.prosecdef.label("security_definer"), |
357 | | - cast(pg_proc.c.prokind, Text).label("kind"), |
358 | | - func.pg_get_function_arguments(pg_proc.c.oid).label("parameters"), |
359 | | - cast(pg_proc.c.provolatile, Text).label("volatility"), |
360 | | - func.pg_get_function_result(pg_proc.c.oid).label("return_type_string"), |
361 | | - pg_proc.c.proargnames.label("arg_names"), |
362 | | - pg_proc.c.proargmodes.label("arg_modes"), |
363 | | - func.coalesce( |
364 | | - get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) |
365 | | - ).label("arg_types"), |
366 | | - func.pg_get_expr( |
367 | | - pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) |
368 | | - ).label("arg_defaults"), |
369 | | - ) |
370 | | - .select_from( |
371 | | - pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) |
372 | | - .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) |
373 | | - .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) |
| 352 | + |
| 353 | +def get_functions_query(version_info): |
| 354 | + source = get_source_column(version_info) |
| 355 | + return ( |
| 356 | + select( |
| 357 | + pg_proc.c.proname.label("name"), |
| 358 | + pg_namespace.c.nspname.label("schema"), |
| 359 | + pg_language.c.lanname.label("language"), |
| 360 | + pg_type.c.typname.label("base_return_type"), |
| 361 | + source.label("source"), |
| 362 | + pg_proc.c.prosecdef.label("security_definer"), |
| 363 | + cast(pg_proc.c.prokind, Text).label("kind"), |
| 364 | + func.pg_get_function_arguments(pg_proc.c.oid).label("parameters"), |
| 365 | + cast(pg_proc.c.provolatile, Text).label("volatility"), |
| 366 | + func.pg_get_function_result(pg_proc.c.oid).label("return_type_string"), |
| 367 | + pg_proc.c.proargnames.label("arg_names"), |
| 368 | + pg_proc.c.proargmodes.label("arg_modes"), |
| 369 | + func.coalesce( |
| 370 | + get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) |
| 371 | + ).label("arg_types"), |
| 372 | + func.pg_get_expr( |
| 373 | + pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) |
| 374 | + ).label("arg_defaults"), |
| 375 | + ) |
| 376 | + .select_from( |
| 377 | + pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) |
| 378 | + .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) |
| 379 | + .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) |
| 380 | + ) |
| 381 | + .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) |
| 382 | + .where(pg_proc.c.prokind != "p") |
| 383 | + .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) |
374 | 384 | ) |
375 | | - .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) |
376 | | - .where(pg_proc.c.prokind != "p") |
377 | | - .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) |
378 | | -) |
| 385 | + |
| 386 | + |
| 387 | +def get_source_column(version_info): |
| 388 | + """Postgres 14 introduced SQL-standard function and procedure bodies. |
| 389 | +
|
| 390 | + When writing a function or procedure in SQL-standard syntax, the body is parsed |
| 391 | + immediately and stored as a parse tree. This allows better tracking of function |
| 392 | + dependencies, and can have security benefits. |
| 393 | +
|
| 394 | + For these sqlbody functions, the pg_proc.prosrc column is an empty string. |
| 395 | + The pre-parsed SQL function body is stored in pg_proc.prosqlbody as pg_node_tree. |
| 396 | + The text representation can be returned along with the full ddl with |
| 397 | + pg_get_functiondef. |
| 398 | + Alternatively pg_get_function_sqlbody(pg_proc.oid) can be called to just get the |
| 399 | + body. This function is not documented, see source: |
| 400 | + https://doxygen.postgresql.org/ruleutils_8c.html#a99a3f975518b6b1707a3159c5f80427e |
| 401 | + """ |
| 402 | + if version_info >= (14, 0): |
| 403 | + return coalesce(func.pg_get_function_sqlbody(pg_proc.c.oid), pg_proc.c.prosrc) |
| 404 | + return pg_proc.c.prosrc |
379 | 405 |
|
380 | 406 |
|
381 | 407 | rel_nsp = pg_namespace.alias("rel_nsp") |
|
0 commit comments