11. Creating stored procedures and triggers
Prev |  | Next

## 11. Creating stored procedures and triggers

SQL Workbench/J will split statements based on the SQL terminator `;` and send each statement unaltered to the DBMS.

When executing statements such as `CREATE PROCEDURE` which in turn contain valid SQL statements, delimited with a `;` the SQL Workbench/J will send everything up to the first semicolon to the backend (because the `;` terminates the SQL statement) In case of a `CREATE PROCEDURE` statement this will obviously result in an error as the statement is not complete.

To be able to run DDL statements with embedded `;` characters, SQL Workbench/J needs to know where a statements ends. To specify the end of a statement with embedded `;` the so called "alternate delimiter" is used. This chapter describes how the alternate delimiter is used by SQL Workbench/J

### 11.1. PostgreSQL

The body of a function in Postgres is a character literal. Because a delimiter inside a character literal does not define the end of the statement, no special treatment is needed for Postgres.

### 11.2. Oracle PL/SQL

This is an example of a `CREATE PROCEDURE` which will NOT work due to the embedded semicolon in the procedure source itself.

    CREATE OR REPLACE FUNCTION proc_sample RETURN INTEGER
    IS
      l_result INTEGER;
    BEGIN
      SELECT max(col1) INTO l_result FROM sometable;
      RETURN l_result;
    END;

When executing this script, Oracle would return an error because SQL Workbench/J will send everything up to the keyword `INTEGER` to the database. Obviously that fragment would not be correct.

The solution is to terminate the script with a character sequence that is called the "alternate delimiter" which can be defined in the connection profile. To be compatible with SQL Developer and SQL*Plus it is recommended to set the alternate delimiter to a forward slash (`/`).

The script needs to be written like this:

    CREATE OR REPLACE FUNCTION proc_sample RETURN INTEGER
    IS
      l_result INTEGER;
    BEGIN
      SELECT max(col1) INTO l_result FROM sometable;
      RETURN l_result;
    END;
    /

Note the trailing forward slash (`/`) at the end in order to "turn on" the use of the alternate delimiter. If you run scripts with embedded semicolons and you get an error, please verify the setting for your alternate delimiter.

The standard delimiter (the semicolon) and the alternate delimiter can be mixed in a single script. Whenever a PL/SQL block (either a stored procedure or an anonymous block) is encountered, SQL Workbench/J expects the alternated delimiter to terminate that block. This follows the same rules as used in SQL*Plus.

The following script will therefore work when connected to an Oracle database:

    drop table sometable cascade constraints;
    create table sometable
    (
      col1 integer not null
    );

    create or replace function proc_sample return integer
    is
      l_result integer;
    begin
      select max(col1) into l_result from sometable;
      return l_result;
    end;
    /

### 11.3. Using a static alternate delimiter

When is the alternate delimiter used?

For all other DBMS, the use of the alternate delimiter is defined by the last delimiter used in the script.

As soon as the statement (or script) that you execute ends with the alternate delimiter, the alternate delimiter is used to separate all SQL statements. When you execute selected text from the editor, be sure to select the alternate delimiter as well, otherwise it will not be recognized (if the alternate delimiter is not selected, the statement to be executed does not end with the alternate delimiter).

This means a script must use the alternate delimiter for all statements in the script. The following script will not work, because the last statement is terminated with the alternate delimiter and thus SQL Workbench/J assumes all statements are delimited with that. As the `CREATE TABLES` statements are delimited with the standard delimiter, they are not recognized as a separate statement and thus the script is sent as a single statement to the server.

    create table orders
    (
      order_id    integer not null primary key,
      customer_id integer not null,
      product_id  integer not null,
      pieces      integer not null,
      order_date  date    not null
    );

    create table orders_audit_log
    (
      order_id    integer not null,
      delete_date timestamp not null
    );

    create trigger orders_audit_log
      for orders
      before delete
    as
    begin
      insert into audit_log (id, delete_date) values (old.order_id, current_timestamp);
    end;
    /

The solution is to terminate every statement with the alternate delimiter:

    create table orders
    (
      order_id    integer not null primary key,
      customer_id integer not null,
      product_id  integer not null,
      pieces      integer not null,
      order_date  date    not null
    )
    /

    create table orders_audit_log
    (
      order_id    integer not null,
      delete_date timestamp not null
    )
    /

    create trigger orders_audit_log
      for orders
      before delete
    as
    begin
      insert into audit_log (id, delete_date) values (old.order_id, current_timestamp);
    end;
    /

### 11.4. Using a dynamic delimiter

The SQL Workbench/J specific command `WbDelimiter` can be used to change the currently used delimiter dynamically inside a script.

Using `WbDelimiter` only works if no alternate delimiter is configured for the current connection - neither through the connection profile nor through the global options.

The `WbDelimiter` keyword must be written as the only command in a line and followed by delimiter to be used after this line. The specified delimiter must then be used on a line of its own to terminate a statement.

    create table orders
    (
      order_id    integer not null primary key,
      customer_id integer not null,
      product_id  integer not null,
      pieces      integer not null,
      order_date  date    not null
    );

    create table orders_audit_log
    (
      order_id    integer not null,
      delete_date timestamp not null
    );

    WbDelimiter ^

    create trigger orders_audit_log
      for orders
      before delete
    as
    begin
      insert into audit_log (id, delete_date) values (old.order_id, current_timestamp);
    end;
    ^

    WbDelimiter ;

    create table customer
    (
      customer_id integer not null primary key,
      name        varchar(100) not null
    );

    insert into customer (customer_id, name)
    values
    (1, 'Arthur'),
    (2, 'Ford');

Prev |  | Next
10. Working with bookmarks | Home | 12. Using SQL Workbench/J