Oracle 技术网

通过 Visual Studio 调试 Oracle PL/SQL

本教程介绍如何通过 Visual Studio 调试 Oracle PL/SQL。

大约 30 分钟

本教程包括下列主题:

将鼠标置于此图标上可以加载和查看本教程的所有屏幕截图。 (警告:因为此操作会同时加载所有屏幕截图,所以网速较慢时,响应时间可能会比较长。)

注:此外,您还可以在下列步骤中将鼠标放在每个单独的图标上,从而仅加载和查看与该步骤相关的屏幕截图。可以通过单击各个屏幕截图来将其隐藏。

利用 ODT 10.2.0.2 中新的、集成的 PL/SQL 调试程序,您仍然可以在 Visual Studio 内部对 .NET 和 Oracle 解决方案进行端到端的调试。现在,您可以在 Visual Studio 环境中按照调试 C# 或 VB.NET 代码的方式调试 PL/SQL 代码,例如过程和函数(独立的和程序包的)、对象方法和触发器。通过 ODT 集成的 PL/SQL 调试程序,您可以使用传统的调试特性,例如设置断点、查看和修改变量值以及检查调用堆栈。

可以在以下三种模式中的一种模式下使用 PL/SQL 调试程序:

直接数据库调试
外部应用程序调试
多层应用程序调试 开发 .NET 应用程序时最有用的选项是多层应用程序调试模式。利用该模式,您能以无缝方式从 Visual Studio 解决方案内部调试 .NET 和 PL/SQL 代码。可以直接从 .NET 代码进入 PL/SQL 代码,然后再返回 .NET 代码。

在本教程中,您将使用多层应用程序模式。在该模式下,您可以作为开发人员在调试会话时同时使用 .NET 和 PL/SQL 代码。

开始创建应用程序之前,您应打开 Visual Studio 并创建连接。执行以下步骤:

1.

选择 开始 > 程序 > Microsoft Visual Studio 2005 > Microsoft Visual Studio 2005

2.

选择 View > Server Explorer

3.

右键单击 Data Connections 并选择 Add Connection...

4.

在 Add Connection 对话框中,首先选择相应的数据源提供程序。

注: 如果数据源已设为 Oracle 数据库服务器 (Oracle ODP.NET),则继续下一步或单击 Change 选择数据提供程序。

在 Change Data Source 对话框中,选择 Oracle Database Server 作为 Data Source,并选择 Oracle Data Provider for .NET 作为 Data Provider。单击 OK

5.

在数据源名下拉菜单中,选择 Oracle 数据库实例的 。为 User name 和 Password 输入 hr,然后单击 Save password 并单击 Test connection

5.

测试连接成功。单击 OK

6.

单击 OK

7.

HR 连接已创建。同样,您还需创建一个 SYSTEM 连接。右键单击 Data Connections 并选择 Add Connection...

8.

数据源提供程序应设为 Oracle 数据库服务器 (Oracle ODP.NET)。在数据源名下拉菜单中,选择 Oracle 数据库实例的

输入 SYSTEM 作为用户名并输入系统口令,单击 Save password,选择 SYSDBA 作为 Role,然后单击 Test connection

9.

测试连接成功。单击 OK

10.

单击 OK

11.

SYSTEM 连接已创建。展开 hr.ORCL 和 SYSTEM.ORCL 以建立连接。

您需要首先准许调试连接会话并调试 HR 用户的任一过程,然后才能使用调试程序。执行以下步骤:

1.

在 Server Explorer 面板中, 右键单击 SYSTEM.ORCL 连接并选择 Query Window

2.

输入下列命令,然后单击 Execute

GRANT debug any procedure, debug connect session TO hr;

注:如果使用的是 Oracle 数据库版本 9.2,则应该执行 grant debug any procedure to hr;

3.

该语句成功执行。

在本主题中,您将创建一个 PL/SQL 程序包和程序包主体以确定 PL/SQL 数组中的每个数字是否是质数,然后在 JOBS 表中使用 PL/SQL 记录创建一个新行。执行以下步骤:

1.

在 Server Explorer 面板中,右键单击 HR.ORCL 连接并选择 Query Window

2.

复制以下代码并将其粘贴到查询窗口中,然后单击 Execute

CREATE OR REPLACE PACKAGE "HR"."OBE" IS
 -- types for associative arrays that client will pass as arguments
 TYPE "T_IN_VALUES" IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
 TYPE "T_OUT_VALUES" IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
   
   -- procedure that accepts two associative arrays
   -- determines if an element is likely prime and
   -- sets value in output array
   PROCEDURE "DETERMINE_PRIMES" ("P_IN_VALUES" IN T_IN_VALUES,
"P_OUT_VALUES" OUT T_OUT_VALUES); -- function that determines if a number is likely prime FUNCTION "IS_PRIME" ("P_NUMBER" IN NUMBER) RETURN NUMBER; -- constants used to return values from function IS_NOT_A_PRIME CONSTANT NUMBER DEFAULT 0; IS_A_PRIME CONSTANT NUMBER DEFAULT 1; -- pl/sql record type for the jobs table "JOBS_REC" jobs%rowtype; -- pl/sql procedure to add new job to jobs table PROCEDURE "ADD_NEW_JOB" ("P_JOB_ID" IN JOBS.JOB_ID%TYPE, "P_JOB_TITLE" IN JOBS.JOB_TITLE%TYPE, "P_MIN_SALARY" IN JOBS.MIN_SALARY%TYPE, "P_MAX_SALARY" IN JOBS.MAX_SALARY%TYPE); END "OBE";

3.

PL/SQL 程序包成功执行。

4.

复制以下代码并将其粘贴到查询窗口中,然后单击 Execute

CREATE OR REPLACE PACKAGE BODY "HR"."OBE" IS
-- procedure that processes the incoming associative arrays
-- calls the method IS_PRIME to determine if element is likely prime
PROCEDURE "DETERMINE_PRIMES" ("P_IN_VALUES" IN T_IN_VALUES,
"P_OUT_VALUES" OUT T_OUT_VALUES) IS
BEGIN
-- loop through each element in the incoming array
-- and set the value for the corresponding element
-- in the out array
for i in p_in_values.first..p_in_values.last
loop
p_out_values(i) := is_prime(p_in_values(i));
end loop;
END "DETERMINE_PRIMES";
-- private function to determine if a number is likely prime
FUNCTION "IS_PRIME" ("P_NUMBER" IN NUMBER) RETURN NUMBER IS
l_sqrt number := 0;
l_sqrt_ceil number := 0;
l_divisor number := 0;
l_divisor_squared number := 0;
begin
-- prime numbers must be >= 2
if p_number < 2 then
return IS_NOT_A_PRIME;
end if;
-- only integers can be prime
if p_number != ceil(p_number) then
return IS_NOT_A_PRIME;
end if;
-- 2 is the only even prime, so it is a special case
if p_number = 2 then
return IS_A_PRIME;
end if;
-- eliminate all other even numbers
if mod(p_number,2) = 0 then
return IS_NOT_A_PRIME;
end if;
-- if the sqrt of the number is an integer, the number is not prime
l_sqrt := sqrt(p_number);
l_sqrt_ceil := ceil(l_sqrt);
if l_sqrt = l_sqrt_ceil then
return IS_NOT_A_PRIME;
end if;
-- the number has passed the basic elimination tests and may be prime
-- loop through set of odd divisors to determine if number is prime
l_divisor := 3;
for i in 1..l_sqrt_ceil
loop
l_divisor_squared := l_divisor * l_divisor;
-- if l_divisor is a factor of p_number, then not a prime
if mod(p_number,l_divisor) = 0 and l_divisor_squared < p_number then
return IS_NOT_A_PRIME;
end if;
-- no factor found, therefore number is likely a prime
if l_divisor_squared > p_number then
return IS_A_PRIME;
end if;
l_divisor := l_divisor + 2;
end loop;
END "IS_PRIME";
-- pl/sql procedure to add new job to jobs table
PROCEDURE "ADD_NEW_JOB" ("P_JOB_ID" IN JOBS.JOB_ID%TYPE,
"P_JOB_TITLE" IN JOBS.JOB_TITLE%TYPE,
"P_MIN_SALARY" IN JOBS.MIN_SALARY%TYPE,
"P_MAX_SALARY" IN JOBS.MAX_SALARY%TYPE) IS
BEGIN
-- use the package variable JOBS_REC to create new record
jobs_rec.job_id := p_job_id;
jobs_rec.job_title := p_job_title;
jobs_rec.min_salary := p_min_salary;
jobs_rec.max_salary := p_max_salary;
-- insert the job record into the table
insert into jobs (job_id, job_title, min_salary, max_salary)
values (jobs_rec.job_id, jobs_rec.job_title,
jobs_rec.min_salary, jobs_rec.max_salary);
END "ADD_NEW_JOB";
END "OBE";

5.

PL/SQL 程序包主体成功执行。

6.

在 Server Explorer for the HR.ORCL Connection 中,展开 Packages > OBE 查看已创建对象的列表。

7.

右键单击 OBE 程序包并选择 Compile Debug。该操作将启用程序包的调试。

8.

该程序包成功编译。关闭 Output 窗口。然后,单击 Start Page 选项卡。

现在您可以新建一个项目。执行以下步骤:

1.

在 Create 旁边的 Recent Projects 区域中:选择 Project...

2.

接受默认的项目类型 Visual C#,选择模板 Console Application,在 Name 中输入 plsqldebugobe1,并在位置字段中输入存储文件的目录(注意:如果该目录不存在,将创建该目录)。然后单击 OK

3.

项目创建成功。现在,您可以添加引用。

4.

选择 Project > Add Reference...

5.

向下滚动 References 列表,选择 Oracle.DataAccess 并单击 OK

6.

复制以下代码并将其粘贴到 Visual Studio 的 Program.cs 窗口中。右键单击 Program.cs 选项卡,选择 Save Program.cs

using System;
using System.Data;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
namespace plsqldebugobe1
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// constants used to represent values returned
// from the pl/sql procedure call
const int IS_NOT_A_PRIME = 0;
const int IS_A_PRIME = 1;
// display progress message
Console.WriteLine("Testing array for prime numbers...\n");
// connection string: adjust for your environment
string constr = "User Id=hr; Password=hr; Data Source=ORCL; enlist=false; pooling=false";
// create and open connection object
OracleConnection con = new OracleConnection(constr);
con.Open();
// create command object for the function call
OracleCommand cmd = new OracleCommand();
cmd.Connection = con;
cmd.CommandText = "OBE.determine_primes";
// set the proper command type
cmd.CommandType = CommandType.StoredProcedure;
// parameter object for the input array
OracleParameter p_in_values = new OracleParameter();
p_in_values.OracleDbType = OracleDbType.Decimal;
p_in_values.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
p_in_values.Value = new decimal[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
p_in_values.Size = 10;
p_in_values.Direction = ParameterDirection.Input;
// parameter object for the output array
OracleParameter p_out_values = new OracleParameter();
p_out_values.OracleDbType = OracleDbType.Decimal;
p_out_values.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
p_out_values.Value = null;
p_out_values.Size = 10;
p_out_values.Direction = ParameterDirection.Output;
// add parameters to the collection
// they must be added in the proper
// order when using bind by position (the default)
cmd.Parameters.Add(p_in_values);
cmd.Parameters.Add(p_out_values);
// execute the pl/sql procedure to populate output array
cmd.ExecuteNonQuery();
// display results to console window
for (int i = 0; i < p_in_values.Size; i++)
{
foreach (OracleParameter p in cmd.Parameters)
{
// the input array is treated as System.Decimal[]
// within the .NET code
if (p.Value is System.Decimal[])
{
Console.Write("The Number {0} ", ((p.Value as System.Decimal[])[i]).ToString());
}
// the output array is treated as OracleDecimal[]
// within the .NET code
if (p.Value is OracleDecimal[])
{
if (((p.Value as OracleDecimal[])[i]).ToInt32() == IS_NOT_A_PRIME)
{
Console.WriteLine("is not a prime number!");
}
else if (((p.Value as OracleDecimal[])[i]).ToInt32() == IS_A_PRIME)
{
Console.WriteLine("is a prime number!");
}
}
}
}
// display a separator line
Console.WriteLine();
// display progress message
Console.WriteLine("Using PL/SQL record...\n");
// remove parameters from command collection
// and set new command text
cmd.Parameters.Clear();
cmd.CommandText = "oramag.add_new_job";
// parameter object for the job_id
OracleParameter p_job_id = new OracleParameter();
p_job_id.Value = "IT_DBA";
// parameter object for the job_title
OracleParameter p_job_title = new OracleParameter();
p_job_title.Value = "Database Administrator";
// parameter object for the min_salary
OracleParameter p_min_salary = new OracleParameter();
p_min_salary.OracleDbType = OracleDbType.Decimal;
p_min_salary.Value = 10000;
// parameter object for the max_salary
OracleParameter p_max_salary = new OracleParameter();
p_max_salary.OracleDbType = OracleDbType.Decimal;
p_max_salary.Value = 15000;
// add parameters to collection
cmd.Parameters.Add(p_job_id);
cmd.Parameters.Add(p_job_title);
cmd.Parameters.Add(p_min_salary);
cmd.Parameters.Add(p_max_salary);
// execute the pl/sql procedure to add new job
cmd.ExecuteNonQuery();
// display simple message to indicate procedure completed
Console.WriteLine("New job successfully created!");
// display a separator line
Console.WriteLine();
// Simple prompt to prevent the console from closing
// when running from the IDE
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
// clean up objects
p_max_salary.Dispose();
p_min_salary.Dispose();
p_job_title.Dispose();
p_job_id.Dispose();
p_out_values.Dispose();
p_in_values.Dispose();
cmd.Dispose();
con.Dispose();
}
}
}

您需要首先配置几个属性和选项,然后才能使用调试环境。执行以下步骤:

1.

选择 Project > plsqldebugobe1 Properties...

2.

选择 Debug

3.

取消选择 Enable the Visual Studio hosting process,右键单击 plsqldebugobe1 选项卡并选择 Save Selected Items

4.

选择 Tools > Options...

5.

向下滚动并展开 Oracle Developer Tools。选择 PL/SQL Debugging。在 Available 数据库连接中,单击 HR.ORCL 连接前面的复选框。

注意,您将看到 TCP/IP 端口范围。在 PL/SQL 调试期间,Oracle 数据库通过该范围内一个随机端口上的 TCP/IP 连接到 Visual Studio。确保该范围代表您计算机上的开放端口并且防火墙没有阻止这些端口。

单击 OK

6.

选择 Tools > Oracle Application Debugging

如果再次选择 Tools > Oracle Application Debugging,应该看到该菜单项前面有一个复选标记。

返回主题列表

在本主题中,您将在 C# 和 PL/SQL 代码中设置一些断点以在执行期间停止调试程序。执行以下步骤:

1.

单击 Program.cs 选项卡并找到 cmd.ExecuteNonQuery() 语句。

2.

在 Program.cs 代码中的 cmd.Parameters.Add(p_out_values) 语句处,创建一个断点。单击该行的任意位置,右键单击并选择 Breakpoint,然后选择 Insert Breakpoint或者,在要设置断点的位置单击代码窗口最左侧的灰色边缘。

3.

将显示断点指示器。

4.

执行程序包后,创建另一个断点。

5.

在 Oracle Explorer 中,双击 DETERMINE_PRIMES 打开该代码。

6.

在 DETERMINE_PRIMES 过程的 BEGIN 语句后的第一个语句处创建一个断点。

7.

从过程列表中选择 ADD_NEW_JOB

8.

在 BEGIN 语句后的第一个语句处创建一个断点。

9.

单击 Program.cs 选项卡。

返回主题列表

在本主题中,您将使用调试程序来执行程序。执行以下步骤:

1.

现在,您准备调试程序。选择 Debug > Start Debugging..

2.

调试程序在第一个断点处停止。要查看变量及其值,单击窗口底部的 Local 选项卡。查看该断点之前的代码,了解数组绑定参数是如何设置的。

3.

要移动到下一行,单击 Step Over 图标。

4.

现在,您位于执行 PL/SQL 过程的语句。单击 Step Over 图标。

5.

下一个断点到达 DETERMINE_PRIMES 过程。注意,P_IN_VALUES 是一个长度为 10 的数组,因此该循环将执行 10 次。单击 Continue 图标若干次,查看 Local 窗口中值的更改。

6.

单击 Locals 窗口中 P_IN_VALUES 变量名旁边的 + 图标,查看输入数组的内容。这是从 C# 应用程序传递到该存储过程的值的数组。继续在代码中停留一段时间。还可以展开 P_OUT_VALUES 数组,观察该数组是否已由此存储过程最终返回的值填充。

7.

单击右下窗口中的 Call Stack 选项卡。通过检查调用堆栈,您可以在执行程序时确定到当前点的代码路径。

8.

禁用该断点以便程序继续到下一个断点。右键单击该断点并选择 Disable Breakpoint您还可以直接单击红色圆圈断点图标,删除该断点。

9.

单击 Continue 图标,让程序继续执行到下一个断点。

10.

代码执行逻辑以生成向用户显示的内容。单击 Step Over 若干次。

11.

再次单击 Continue 图标,转到下一个断点。

12.

到达 ADD_NEW_JOB 过程断点。

13.

如果要查看 JOBS_REC PL/SQL 记录全局变量,您需要创建一个监视。选择 JOBS_REC 并右键单击,然后选择 Add Watch。然后,选择窗口底部的 Watch 选项卡。

14.

Watch 窗口是 Visual Studio 中的一个内置窗口,通过该窗口,您可以检查特定的程序变量。展开 JOBS_REC Watch。

15.

单击 Step Over 4 次以查看填充的 Watch。然后,单击 Continue

16.

程序完成执行后,将显示结果。

返回主题列表

在本教程中,您学习了如何:

添加到数据库的连接并授予调试权限
创建程序包和程序包主体
创建项目并配置调试环境
调试 PL/SQL

返回主题列表

将鼠标移到该图标上可以隐藏所有屏幕截图。