diff --git a/cmd/dump/dump_permission_integration_test.go b/cmd/dump/dump_permission_integration_test.go index b9ba07f3..bf099e2a 100644 --- a/cmd/dump/dump_permission_integration_test.go +++ b/cmd/dump/dump_permission_integration_test.go @@ -35,20 +35,12 @@ func TestDumpCommand_PermissionSuite(t *testing.T) { defer container.Terminate(ctx, t) // Run each permission test with its own isolated database - t.Run("ProcedurePermissionError", func(t *testing.T) { - testProcedurePermission(t, ctx, container, "testdb_proc") + t.Run("ProcedureAndFunctionSourceAccess", func(t *testing.T) { + testProcedureAndFunctionSourceAccess(t, ctx, container, "testdb_source") }) - t.Run("FunctionPermissionError", func(t *testing.T) { - testFunctionPermission(t, ctx, container, "testdb_func") - }) - - t.Run("MixedAccessibilityObjects", func(t *testing.T) { - testMixedAccessibility(t, ctx, container, "testdb_mixed") - }) - - t.Run("IgnoredObjectsWithPermissionIssues", func(t *testing.T) { - testIgnoredObjectsWithPermissions(t, ctx, container, "testdb_ignore") + t.Run("IgnoredObjects", func(t *testing.T) { + testIgnoredObjects(t, ctx, container, "testdb_ignore") }) } @@ -109,190 +101,8 @@ func getRoleNames(dbName string) (restrictedRole string, regularUser string) { return fmt.Sprintf("restricted_owner_%s", dbName), fmt.Sprintf("regular_user_%s", dbName) } -// testProcedurePermission tests procedure owned by restricted role -func testProcedurePermission(t *testing.T, ctx context.Context, container *testutil.ContainerInfo, dbName string) { - // Setup isolated database - dbConn := setupTestDatabase(ctx, t, container, dbName) - defer dbConn.Close() - - // Get role names for this database - restrictedRole, regularUser := getRoleNames(dbName) - - // Create procedure owned by restricted role - _, err := dbConn.ExecContext(ctx, fmt.Sprintf(` - -- Create a procedure in public schema owned by the restricted role - CREATE OR REPLACE PROCEDURE public.test_procedure(param_name TEXT) - LANGUAGE plpgsql - AS $$ - BEGIN - RAISE NOTICE 'This procedure is owned by restricted_owner: %%', param_name; - END; - $$; - - -- Change ownership to the restricted role - ALTER PROCEDURE public.test_procedure(TEXT) OWNER TO %s; - `, restrictedRole)) - if err != nil { - t.Fatalf("Failed to setup permission test scenario: %v", err) - } - - // Try to dump schema as regular_user (should fail with permission error) - output, err := executeDumpCommandAsUser( - container.Host, - container.Port, - dbName, - regularUser, - "userpass", - "public", - ) - - // Check that output does not contain formatting errors - if strings.Contains(output, "%!s()") { - t.Errorf("Found formatting error '%s' in dump output, expected proper error handling", "%!s()") - t.Logf("Full output:\n%s", output) - } - - // Expected behavior: Should return an error about permission denied - if err == nil { - t.Errorf("Expected permission-related error, but dump succeeded") - } else if !strings.Contains(err.Error(), "permission denied") { - t.Errorf("Expected 'permission denied' error, got: %v", err) - } - - if err != nil { - // Expected error due to permission restrictions - _ = err - } -} - -// testFunctionPermission tests function owned by restricted role -func testFunctionPermission(t *testing.T, ctx context.Context, container *testutil.ContainerInfo, dbName string) { - // Setup isolated database - dbConn := setupTestDatabase(ctx, t, container, dbName) - defer dbConn.Close() - - // Get role names for this database - restrictedRole, regularUser := getRoleNames(dbName) - - // Create function owned by restricted role - _, err := dbConn.ExecContext(ctx, fmt.Sprintf(` - -- Create a function in public schema owned by the restricted role - CREATE OR REPLACE FUNCTION public.test_function(input_text TEXT) - RETURNS TEXT - LANGUAGE plpgsql - AS $$ - BEGIN - RETURN 'Processed: ' || input_text; - END; - $$; - - -- Change ownership to the restricted role - ALTER FUNCTION public.test_function(TEXT) OWNER TO %s; - `, restrictedRole)) - if err != nil { - t.Fatalf("Failed to setup function permission test: %v", err) - } - - // Try to dump schema as regular_user (should fail with permission error) - output, err := executeDumpCommandAsUser( - container.Host, - container.Port, - dbName, - regularUser, - "userpass", - "public", - ) - - // Check that output does not contain formatting errors - if strings.Contains(output, "%!s()") { - t.Errorf("Found formatting error '%s' in function dump output, expected proper error handling", "%!s()") - t.Logf("Full output:\n%s", output) - } - - // Expected behavior: Should return an error about permission denied - if err == nil { - t.Errorf("Expected permission-related error for inaccessible function, but dump succeeded") - } else if !strings.Contains(err.Error(), "permission denied") { - t.Errorf("Expected 'permission denied' error for function, got: %v", err) - } - - if err != nil { - // Expected error due to permission restrictions - _ = err - } -} - -// testMixedAccessibility tests mixed accessible and inaccessible objects -func testMixedAccessibility(t *testing.T, ctx context.Context, container *testutil.ContainerInfo, dbName string) { - // Setup isolated database - dbConn := setupTestDatabase(ctx, t, container, dbName) - defer dbConn.Close() - - // Get role names for this database - restrictedRole, regularUser := getRoleNames(dbName) - - // Create both accessible and inaccessible procedures - _, err := dbConn.ExecContext(ctx, fmt.Sprintf(` - -- Create accessible procedure owned by regular_user - CREATE OR REPLACE PROCEDURE public.accessible_procedure() - LANGUAGE plpgsql - AS $$ - BEGIN - RAISE NOTICE 'This procedure is accessible'; - END; - $$; - - ALTER PROCEDURE accessible_procedure() OWNER TO %s; - - -- Create inaccessible procedure owned by restricted_owner - CREATE OR REPLACE PROCEDURE public.inaccessible_procedure(test_param INTEGER) - LANGUAGE plpgsql - AS $$ - BEGIN - RAISE NOTICE 'This procedure should not be accessible: %%', test_param; - END; - $$; - - ALTER PROCEDURE inaccessible_procedure(INTEGER) OWNER TO %s; - `, regularUser, restrictedRole)) - if err != nil { - t.Fatalf("Failed to setup mixed permission test: %v", err) - } - - // Try to dump schema as regular_user - output, err := executeDumpCommandAsUser( - container.Host, - container.Port, - dbName, - regularUser, - "userpass", - "public", - ) - - // Should not contain formatting errors - if strings.Contains(output, "%!s()") { - t.Errorf("Found formatting error '%s' in mixed dump output", "%!s()") - } - - // Since we abort on permission errors, the dump should fail entirely - // when encountering the inaccessible procedure - if err == nil { - t.Errorf("Expected permission-related error due to inaccessible procedure, but dump succeeded") - } else if !strings.Contains(err.Error(), "permission denied") { - t.Errorf("Expected 'permission denied' error, got: %v", err) - } - - // The accessible procedure won't be in output because we abort on the first permission error - // This is the expected behavior: fail fast rather than silently skip - - if err != nil { - // Expected error due to permission restrictions - _ = err - } -} - -// testIgnoredObjectsWithPermissions tests procedures ignored via .pgschemaignore -func testIgnoredObjectsWithPermissions(t *testing.T, ctx context.Context, container *testutil.ContainerInfo, dbName string) { +// testIgnoredObjects tests procedures ignored via .pgschemaignore +func testIgnoredObjects(t *testing.T, ctx context.Context, container *testutil.ContainerInfo, dbName string) { // This test verifies that when procedures/functions are explicitly ignored // via .pgschemaignore, permission issues should not cause the dump to fail @@ -378,37 +188,22 @@ patterns = ["skip_func_restricted"] t.Errorf("Found formatting error '%s' in dump output with ignored objects", "%!s()") } - // The dump should still fail because of the non-ignored restricted procedure - if err == nil { - t.Errorf("Expected permission-related error due to non-ignored restricted procedure, but dump succeeded") - } else if !strings.Contains(err.Error(), "permission denied") { - t.Errorf("Expected 'permission denied' error, got: %v", err) - } - - // Verify the error is specifically about the non-ignored procedure - if err != nil && !strings.Contains(err.Error(), "check_proc_restricted") { - t.Errorf("Expected error about 'check_proc_restricted', got: %v", err) - } - - // The ignored procedures/functions should not appear in the error message + // The dump should now succeed since all procedures are accessible via pg_get_functiondef if err != nil { - if strings.Contains(err.Error(), "skip_proc_restricted") { - t.Errorf("Error should not mention skip_proc_restricted (it should be skipped), got: %v", err) + t.Errorf("Dump should succeed since all procedures are readable via pg_get_functiondef, got error: %v", err) + } else { + t.Logf("Success: dump succeeded with ignored/non-ignored procedures") + // Verify the non-ignored procedure is in output + if !strings.Contains(output, "check_proc_restricted") { + t.Errorf("Expected check_proc_restricted in output") } - if strings.Contains(err.Error(), "skip_func_restricted") { - t.Errorf("Error should not mention skip_func_restricted (it should be skipped), got: %v", err) + // Verify the ignored procedures are NOT in output (due to ignore rules) + if strings.Contains(output, "skip_proc_restricted") { + t.Errorf("skip_proc_restricted should be ignored and not appear in output") + } + if strings.Contains(output, "skip_func_restricted") { + t.Errorf("skip_func_restricted should be ignored and not appear in output") } - } - - // Verify that the ignored objects were indeed skipped successfully - // The error should only be about the non-ignored procedure - if err != nil && strings.Contains(err.Error(), "check_proc_restricted") { - // Expected: correctly failed only on non-ignored restricted procedure - } - - if err != nil { - // Expected error due to non-ignored restricted object - _ = err } // Additional test: Create .pgschemaignore that ignores ALL restricted objects @@ -424,7 +219,7 @@ patterns = ["*_restricted"] t.Fatalf("Failed to create comprehensive .pgschemaignore file: %v", err) } - // Now the dump should succeed because all restricted objects are ignored + // Now the dump should succeed and ALL restricted objects should be ignored output2, err2 := executeDumpCommandAsUser( container.Host, container.Port, @@ -435,17 +230,204 @@ patterns = ["*_restricted"] ) if err2 != nil { - t.Errorf("Expected dump to succeed when all restricted objects are ignored, got error: %v", err2) + t.Errorf("Dump should succeed when all restricted objects are ignored, got error: %v", err2) + } else { + t.Logf("Success: dump succeeded with all restricted objects ignored") + // Verify that ALL restricted objects are ignored + if strings.Contains(output2, "skip_proc_restricted") { + t.Errorf("skip_proc_restricted should be ignored and not appear in output") + } + if strings.Contains(output2, "skip_func_restricted") { + t.Errorf("skip_func_restricted should be ignored and not appear in output") + } + if strings.Contains(output2, "check_proc_restricted") { + t.Errorf("check_proc_restricted should be ignored and not appear in output") + } } // Should not contain formatting errors if strings.Contains(output2, "%!s()") { t.Errorf("Found formatting error '%s' in comprehensive ignore dump output", "%!s()") } +} + +// testProcedureAndFunctionSourceAccess tests that procedure and function source code is readable +// via p.prosrc even when information_schema.routines.routine_definition is NULL +func testProcedureAndFunctionSourceAccess(t *testing.T, ctx context.Context, container *testutil.ContainerInfo, dbName string) { + // Setup isolated database + dbConn := setupTestDatabase(ctx, t, container, dbName) + defer dbConn.Close() + + // Get role names for this database + restrictedRole, regularUser := getRoleNames(dbName) + + // Create both procedure and function owned by restricted role + _, err := dbConn.ExecContext(ctx, fmt.Sprintf(` + -- Create a procedure owned by the restricted role + CREATE OR REPLACE PROCEDURE public.test_source_visibility_proc(param_name TEXT) + LANGUAGE plpgsql + AS $$ + BEGIN + RAISE NOTICE 'Procedure source visibility test: %%', param_name; + END; + $$; + + -- Create a function owned by the restricted role + CREATE OR REPLACE FUNCTION public.test_source_visibility_func(param_name TEXT) + RETURNS TEXT + LANGUAGE plpgsql + AS $$ + BEGIN + RETURN 'Function source visibility test: ' || param_name; + END; + $$; + + -- Change ownership to the restricted role + ALTER PROCEDURE public.test_source_visibility_proc(TEXT) OWNER TO %s; + ALTER FUNCTION public.test_source_visibility_func(TEXT) OWNER TO %s; + `, restrictedRole, restrictedRole)) + if err != nil { + t.Fatalf("Failed to setup procedure and function for source visibility test: %v", err) + } + + // Connect as the regular user to test visibility + config := &util.ConnectionConfig{ + Host: container.Host, + Port: container.Port, + Database: dbName, + User: regularUser, + Password: "userpass", + SSLMode: "prefer", + ApplicationName: "pgschema", + } + + regularUserConn, err := util.Connect(config) + if err != nil { + t.Fatalf("Failed to connect as regular user: %v", err) + } + defer regularUserConn.Close() + + // Test 1: Check that information_schema.routines.routine_definition can be NULL for procedures + var procRoutineDefinition sql.NullString + err = regularUserConn.QueryRowContext(ctx, ` + SELECT routine_definition + FROM information_schema.routines + WHERE routine_schema = 'public' + AND routine_name = 'test_source_visibility_proc' + AND routine_type = 'PROCEDURE' + `).Scan(&procRoutineDefinition) + + if err != nil { + t.Fatalf("Failed to query procedure routine_definition: %v", err) + } + + // Test 2: Check that information_schema.routines.routine_definition can be NULL for functions + var funcRoutineDefinition sql.NullString + err = regularUserConn.QueryRowContext(ctx, ` + SELECT routine_definition + FROM information_schema.routines + WHERE routine_schema = 'public' + AND routine_name = 'test_source_visibility_func' + AND routine_type = 'FUNCTION' + `).Scan(&funcRoutineDefinition) + + if err != nil { + t.Fatalf("Failed to query function routine_definition: %v", err) + } + + // Test 3: Check that pg_get_functiondef() works for procedures despite NULL routine_definition + var procedureDef string + err = regularUserConn.QueryRowContext(ctx, ` + SELECT pg_get_functiondef(p.oid) + FROM pg_proc p + JOIN pg_namespace n ON p.pronamespace = n.oid + WHERE n.nspname = 'public' + AND p.proname = 'test_source_visibility_proc' + `).Scan(&procedureDef) + + if err != nil { + t.Errorf("pg_get_functiondef should work for procedures but failed: %v", err) + } else { + // Verify we got the actual procedure definition + if !strings.Contains(procedureDef, "CREATE OR REPLACE PROCEDURE") { + t.Errorf("Expected complete CREATE PROCEDURE statement, got: %s", procedureDef) + } + if !strings.Contains(procedureDef, "Procedure source visibility test") { + t.Errorf("Expected procedure body content, got: %s", procedureDef) + } + t.Logf("Success: pg_get_functiondef returned procedure: %s", procedureDef) + } - if err2 == nil { - // Expected: dump succeeded when all restricted objects were ignored + // Test 4: Check that pg_get_functiondef() works for functions despite NULL routine_definition + var functionDef string + err = regularUserConn.QueryRowContext(ctx, ` + SELECT pg_get_functiondef(p.oid) + FROM pg_proc p + JOIN pg_namespace n ON p.pronamespace = n.oid + WHERE n.nspname = 'public' + AND p.proname = 'test_source_visibility_func' + `).Scan(&functionDef) + + if err != nil { + t.Errorf("pg_get_functiondef should work for functions but failed: %v", err) + } else { + // Verify we got the actual function definition + if !strings.Contains(functionDef, "CREATE OR REPLACE FUNCTION") { + t.Errorf("Expected complete CREATE FUNCTION statement, got: %s", functionDef) + } + if !strings.Contains(functionDef, "Function source visibility test") { + t.Errorf("Expected function body content, got: %s", functionDef) + } + t.Logf("Success: pg_get_functiondef returned function: %s", functionDef) } + + // Test 5: Try to dump schema as regular_user (should succeed with pg_get_functiondef) + output, dumpErr := executeDumpCommandAsUser( + container.Host, + container.Port, + dbName, + regularUser, + "userpass", + "public", + ) + + // Check that output does not contain formatting errors + if strings.Contains(output, "%!s()") { + t.Errorf("Found formatting error '%s' in dump output", "%!s()") + t.Logf("Full output:\n%s", output) + } + + // The dump should succeed since we use p.prosrc instead of routine_definition + if dumpErr != nil { + t.Errorf("Dump should succeed with p.prosrc, but got error: %v", dumpErr) + t.Logf("Full output:\n%s", output) + } else { + t.Logf("Success: dump succeeded using p.prosrc") + // Verify the output contains both procedure and function definitions + if !strings.Contains(output, "CREATE OR REPLACE PROCEDURE") { + t.Errorf("Expected complete CREATE PROCEDURE statement in output") + } + if !strings.Contains(output, "CREATE OR REPLACE FUNCTION") { + t.Errorf("Expected complete CREATE FUNCTION statement in output") + } + if !strings.Contains(output, "test_source_visibility_proc") { + t.Errorf("Expected procedure name 'test_source_visibility_proc' in output") + } + if !strings.Contains(output, "test_source_visibility_func") { + t.Errorf("Expected function name 'test_source_visibility_func' in output") + } + if !strings.Contains(output, "Procedure source visibility test") { + t.Errorf("Expected procedure body content in output") + } + if !strings.Contains(output, "Function source visibility test") { + t.Errorf("Expected function body content in output") + } + } + + // Summary: This test verifies that: + // 1. information_schema.routines.routine_definition can be NULL for both procedures and functions + // 2. p.prosrc works reliably and returns the source for both procedures and functions + // 3. pgschema now uses p.prosrc and succeeds for both object types } // executeDumpCommandAsUser executes the pgschema dump command as a specific user diff --git a/internal/ir/inspector.go b/internal/ir/inspector.go index d2662080..0181f1ca 100644 --- a/internal/ir/inspector.go +++ b/internal/ir/inspector.go @@ -1098,12 +1098,8 @@ func (i *Inspector) buildFunctions(ctx context.Context, schema *IR, targetSchema continue } - // Check if function definition is accessible - var definition string - if fn.RoutineDefinition == nil { - return fmt.Errorf("permission denied: cannot access function definition for %s.%s (function may be owned by a restricted role)", schemaName, functionName) - } - definition = fn.RoutineDefinition.(string) + // Get function definition from pg_get_functiondef + definition := i.safeInterfaceToString(fn.RoutineDefinition) dbSchema := schema.getOrCreateSchema(schemaName) @@ -1306,12 +1302,8 @@ func (i *Inspector) buildProcedures(ctx context.Context, schema *IR, targetSchem continue } - // Check if procedure definition is accessible - var definition string - if proc.RoutineDefinition == nil { - return fmt.Errorf("permission denied: cannot access procedure definition for %s.%s (procedure may be owned by a restricted role)", schemaName, procedureName) - } - definition = i.safeInterfaceToString(proc.RoutineDefinition) + // Get procedure definition from pg_get_functiondef + definition := i.safeInterfaceToString(proc.RoutineDefinition) dbSchema := schema.getOrCreateSchema(schemaName) diff --git a/internal/queries/dml.sql.go b/internal/queries/dml.sql.go index 85679b3c..51576a56 100644 --- a/internal/queries/dml.sql.go +++ b/internal/queries/dml.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.29.0 +// sqlc v1.30.0 package queries diff --git a/internal/queries/models.sql.go b/internal/queries/models.sql.go index 9caff681..65e4ab86 100644 --- a/internal/queries/models.sql.go +++ b/internal/queries/models.sql.go @@ -1,5 +1,5 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.29.0 +// sqlc v1.30.0 package queries diff --git a/internal/queries/queries.sql b/internal/queries/queries.sql index bd9dcaf9..4a033a1d 100644 --- a/internal/queries/queries.sql +++ b/internal/queries/queries.sql @@ -290,10 +290,10 @@ ORDER BY sequence_schema, sequence_name; -- GetFunctions retrieves all user-defined functions (excluding extension members) -- name: GetFunctions :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, COALESCE(pg_get_function_result(p.oid), r.data_type) AS data_type, r.external_language, @@ -323,10 +323,10 @@ ORDER BY r.routine_schema, r.routine_name; -- GetProcedures retrieves all user-defined procedures (excluding extension members) -- name: GetProcedures :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, r.external_language, COALESCE(desc_proc.description, '') AS procedure_comment, @@ -722,10 +722,10 @@ ORDER BY s.schemaname, s.sequencename; -- GetFunctionsForSchema retrieves all user-defined functions for a specific schema -- name: GetFunctionsForSchema :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, COALESCE(pg_get_function_result(p.oid), r.data_type) AS data_type, r.external_language, @@ -755,10 +755,10 @@ ORDER BY r.routine_schema, r.routine_name; -- GetProceduresForSchema retrieves all user-defined procedures for a specific schema -- name: GetProceduresForSchema :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, r.external_language, COALESCE(desc_proc.description, '') AS procedure_comment, diff --git a/internal/queries/queries.sql.go b/internal/queries/queries.sql.go index 3fa29901..f11e65ae 100644 --- a/internal/queries/queries.sql.go +++ b/internal/queries/queries.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.29.0 +// sqlc v1.30.0 // source: queries.sql package queries @@ -1073,10 +1073,10 @@ func (q *Queries) GetEnumValuesForSchema(ctx context.Context, dollar_1 sql.NullS } const getFunctions = `-- name: GetFunctions :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, COALESCE(pg_get_function_result(p.oid), r.data_type) AS data_type, r.external_language, @@ -1108,7 +1108,7 @@ ORDER BY r.routine_schema, r.routine_name type GetFunctionsRow struct { RoutineSchema interface{} `db:"routine_schema" json:"routine_schema"` RoutineName interface{} `db:"routine_name" json:"routine_name"` - RoutineDefinition interface{} `db:"routine_definition" json:"routine_definition"` + RoutineDefinition string `db:"routine_definition" json:"routine_definition"` RoutineType interface{} `db:"routine_type" json:"routine_type"` DataType sql.NullString `db:"data_type" json:"data_type"` ExternalLanguage interface{} `db:"external_language" json:"external_language"` @@ -1158,10 +1158,10 @@ func (q *Queries) GetFunctions(ctx context.Context) ([]GetFunctionsRow, error) { } const getFunctionsForSchema = `-- name: GetFunctionsForSchema :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, COALESCE(pg_get_function_result(p.oid), r.data_type) AS data_type, r.external_language, @@ -1193,7 +1193,7 @@ ORDER BY r.routine_schema, r.routine_name type GetFunctionsForSchemaRow struct { RoutineSchema interface{} `db:"routine_schema" json:"routine_schema"` RoutineName interface{} `db:"routine_name" json:"routine_name"` - RoutineDefinition interface{} `db:"routine_definition" json:"routine_definition"` + RoutineDefinition string `db:"routine_definition" json:"routine_definition"` RoutineType interface{} `db:"routine_type" json:"routine_type"` DataType sql.NullString `db:"data_type" json:"data_type"` ExternalLanguage interface{} `db:"external_language" json:"external_language"` @@ -1538,10 +1538,10 @@ func (q *Queries) GetPartitionedTablesForSchema(ctx context.Context, dollar_1 sq } const getProcedures = `-- name: GetProcedures :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, r.external_language, COALESCE(desc_proc.description, '') AS procedure_comment, @@ -1564,7 +1564,7 @@ ORDER BY r.routine_schema, r.routine_name type GetProceduresRow struct { RoutineSchema interface{} `db:"routine_schema" json:"routine_schema"` RoutineName interface{} `db:"routine_name" json:"routine_name"` - RoutineDefinition interface{} `db:"routine_definition" json:"routine_definition"` + RoutineDefinition string `db:"routine_definition" json:"routine_definition"` RoutineType interface{} `db:"routine_type" json:"routine_type"` ExternalLanguage interface{} `db:"external_language" json:"external_language"` ProcedureComment sql.NullString `db:"procedure_comment" json:"procedure_comment"` @@ -1606,10 +1606,10 @@ func (q *Queries) GetProcedures(ctx context.Context) ([]GetProceduresRow, error) } const getProceduresForSchema = `-- name: GetProceduresForSchema :many -SELECT +SELECT r.routine_schema, r.routine_name, - r.routine_definition, + p.prosrc AS routine_definition, r.routine_type, r.external_language, COALESCE(desc_proc.description, '') AS procedure_comment, @@ -1629,7 +1629,7 @@ ORDER BY r.routine_schema, r.routine_name type GetProceduresForSchemaRow struct { RoutineSchema interface{} `db:"routine_schema" json:"routine_schema"` RoutineName interface{} `db:"routine_name" json:"routine_name"` - RoutineDefinition interface{} `db:"routine_definition" json:"routine_definition"` + RoutineDefinition string `db:"routine_definition" json:"routine_definition"` RoutineType interface{} `db:"routine_type" json:"routine_type"` ExternalLanguage interface{} `db:"external_language" json:"external_language"` ProcedureComment sql.NullString `db:"procedure_comment" json:"procedure_comment"`