Custom Formatting in SQLDev 4.2


Formatting is one of the software development features that always steers controversy. To put it bluntly, everybody wants to format the code their own way. In previous SQL Dev versions the answer to that challenge was having elaborate tree-table widget spiced with several dozens of various options.

Arguably, the combined SQL and PL/SQL grammar is the most cumbersome programming language in existence, with total number of grammar productions exceeding 20000. From that perspective, the list of “Advanced Format” options offered by SQL Dev feels inadequate. To address this mismatch, the latest release introduced “Custom Format”. It offers to a developer means to specify formal conditions which SQL and PL/SQL grammatical constructs to indent,  align, pad, add extra line breaks and so on.

As usual, the best way to demonstrate a new feature is an example. Consider the following enhancement request by Stefan Poschenrieder to remove the indent of the NOT NULL ENABLE inline constraint in the following code

CREATE TABLE dim_aspect (
    dim_aspectid       NUMBER(11,0)
        NOT NULL ENABLE,
    source             VARCHAR2(50),
    hier1_desc         VARCHAR2(50),
    hierarchy1         VARCHAR2(50),
    dwh_inserteddate   DATE
);

The first step is identifying the target grammar symbol in the Code Outline panel. Here is screen snapshot illustrating it:
code outline.png
As illustrated there, positioning a cursor over the NOT keyword highlights the corresponding node in the parse tree. Therefore, the grammar symbol of interest is probably “inline_constraint“. Clicking onto the node labeled inline_constraint to double check this guess witnesses it highlighting “NOT NULL ENABLE” in the code editor. (Please be aware that clicking onto a hyperlinked looking grammar symbol has side effect of opening documentation page featuring that symbol railroad diagram definition).

As soon as the grammar symbol of interest is known, lets find it at Custom Format preferences panel. Admittedly, glaring omission there is the search widget. Yet, grammar symbols are arranged in lexicographic order, so that a reader would have no trouble spotting inline_constraint in the following formatting rule:

...
| [node) index_subpartition_clause[69,119)# 
| [node) inline_constraint & ![node) inline_constraint[14,67)
| [node) inline_ref_constraint
...

This fragment of rather convoluted disjunctive condition instructs the formatter to indent the nodes with payload “index_subpartition_clause[69,119)#“, or the nodes labeled with “inline_constraint” (but not those that also labeled as “inline_constraint[14,67)“), or the nodes labeled “inline_ref_constraint“.  Therefore, commenting out the middle conjunction

...
| [node) index_subpartition_clause[69,119)# 
--| [node) inline_constraint & ![node) inline_constraint[14,67)
| [node) inline_ref_constraint
...

is likely a solution to the problem. After making this change, a reader is advised to test what effect do modified formatting rules have on a SQL or PL/SQL code sample in the preview editor. This action is performed with the bottom-left “Run” button. The two neighbors “Import…” and “Export…” are for saving and retrieving your work. By default the modified “format.arbori” file is kept in the SQLDev product preferences directory.

Let’s reinforce this custom formatting idea with two more examples, both from Steven Feuerstein. In both cases we’ll learn some additional syntax of that formal formatting rules specification. The first example is just a bug witnessed by the following test case:

CREATE OR REPLACE PACKAGE BODY plch_pkg IS BEGIN
   FOR indx IN 1..100000 LOOP
      g_strings(    
         indx       --<--- ???
      ) := 'String ' || indx;
   END LOOP;
END;
/

The rule responsible for that unwanted indentation of the “indx” parameter is

| [node) pls_expr & [node-1) '(' & ![node) numeric_literal

It requires the parse node to be labeled with “pls_expr” but not “numeric_literal“. Also, it stipulates that the prior sibling node (formally, “node-1“) is labeled with open parenthesis. Adding the condition for the node successor not to be the closing parenthesis

| [node) pls_expr & [node-1) '(' & ![node) numeric_literal & ![node+1) ')' 

is all it takes to fix this snag.

In the second example

CREATE PACKAGE fmt AS
    PROCEDURE create_checklist (
        user_id_in       IN INTEGER,
        question_id_in   IN INTEGER
    );
    --<-- wanted extra line break 
    PROCEDURE remove_checklist (
        checklist_id_in   IN INTEGER
    );

    PROCEDURE remove_checklist (
        user_id_in       IN INTEGER,
        question_id_in   IN INTEGER
    );

END fmt;

the formatted code have only single line breaks after each package member. The formatting rule responsible for this functionality is “extraLines“. It formally a disjunction of the two rules “sql_stmts” and “significant_statements“. In the later rule we see the following conjunctive condition:

| [node) basic_decl_item & [node+20 < node)

The syntax of first part is familiar: it requires the node to be labeled as “basic_decl_item“.

The second part is little more cumbersome. The “[ node” and “node )” refer to the node beginning and ending position, correspondingly. Each node in the parse tree recognizes a sequence of tokens. In our example nodes labeled as “basic_decl_item” recognizes a sequence of tokens beginning with the PROCEDURE keyword and ending with semicolon. The beginning position is the offset of the PROCEDURE keyword, while the ending position is the offset of the semicolon. The condition  “[node+20 < node)” requires more than 20 tokens between the node beginning and ending position, or informally the package member to be of “sufficient length”.

Please note that package data members are recognized as “basic_decl_item” as well. Therefore, the proper fix depends on if a user wants to inset double line breaks after data members or not. The length condition was a naive attempt on distinguishing data and procedures, but more elaborate condition, distinguishing data and procedures is possible too.

Let’s walk trough yet another example demonstrating how flexible it is. Consider the following example adapted from ToadWorld ER:

Can we vertically align THEN values of CASE statement ? Current Format:

SELECT CASE
         WHEN a.col5 = b.col5 THEN 'COL5'
         WHEN a.col55555555555555 = b.col55 THEN 'COL55555'
         WHEN a.col555555 = b.col55555 THEN 'COL555555'
       END AS VAL
FROM   table1;

Desired Format:

SELECT CASE
         WHEN a.col5 = b.col5                THEN 'COL5'
         WHEN a.col55555555555555 = b.col55  THEN 'COL55555'
         WHEN a.col555555   = b.col5555      THEN 'COL555555'
       END AS VAL
FROM   table1;

For the reference, the default formatting in SQLDeveloper looks less than desirable:

SELECT
        CASE WHEN
            a.col5 = b.col5
        THEN
            'COL5'
        WHEN
            a.col55555555555555 = b.col55
        THEN
            'COL55555'
        WHEN
            a.col555555 = b.col55555
        THEN
            'COL555555'
        END
    AS val
FROM
    table1

However, to satisfy this request it requires exactly 4 changes of the formatting program.

First, before taking on alignments, we should fix unwanted indentation. How about not indenting conditions a.col5 = b.col5 and some such? By examining the parse tree, we notice that the code fragment a.col5 = b.col5 is formally a condition. Therefore, it must be that the line

| [node) condition  &  [node-1) 'WHEN'

of the formatting program that is responsible for the effect. Comment (or delete it), and rerun the formatter to confirm this hypothesis:

SELECT
        CASE WHEN a.col5 = b.col5 THEN
            'COL5'
        WHEN a.col55555555555555 = b.col55 THEN
            'COL55555'
        WHEN a.col555555 = b.col55555 THEN
            'COL555555'
        END
    AS val
FROM
    table1;

What about not indenting expressions, like string literal ‘COL5’? Just comment out the following rule:

--| [node) expr  &  [node-1) 'THEN'

The effect of this change, however, is somewhat discouraging:

SELECT
        CASE WHEN a.col5 = b.col5 THEN 'COL5' WHEN a.col55555555555555 = b.col5 THEN 'COL55555' WHEN a.col555555 = b.col5 THEN 'COL555555' WHEN a.col55555555
 = b.col5 THEN 'COL5555555' END
    AS val
FROM
    table1;

Here we need to look onto parse tree yet again to identify what grammar symbol would indent the entire WHEN-THEN fragments. The symbol in question is the  searched_case_expression#, so that the missing indentation rule alternative is just

| [node) searched_case_expression#

With this amendments the sample code formats to:

SELECT
        CASE
            WHEN a.col5 = b.col5 THEN 'COL5'
            WHEN a.col55555555555555 = b.col55 THEN 'COL55555'
            WHEN a.col555555 = b.col5555 THEN 'COL555555'
        END
    AS val
FROM
    table1;

Please note that even though there is a set options for CASE expressions (which, again, is a concession to traditional format design), flipping those options won’t produce format as clean as we just did.

So far we made 3 custom formatting program changes . Now we are ready to take on alignments. Let’s first investigate how current  formatting rules align the equalities,  because reverse engineering similar functionality is the easiest way to master new skills. Here is the code:

/**
 * All alignments (paddings) ...
**/
paddedIdsInScope: (
-- types
  [id) identifier & [id+1) datatype & [scope) relational_properties
| [id) decl_id & ([id+1) prm_spec_unconstrained_type | [id+1) 'IN' | [id+1) 'OUT' ) & [scope) fml_part
| [id) decl_id & ([id+1) constrained_type | [id+1) object_d_rhs) & [scope) adt_definition
| [id) decl_id & ([id+1) constrained_type | [id+1) object_d_rhs) & [scope) decl_list
-- =>
| [id) sim_expr & [id+1) '=' & [id+1+1) '>' & [scope) paren_expr_list
-- :=
| [id) name & [id+1) ':' & [id+1+1) '=' & [scope) seq_of_stmts
-- =
| [id) column & [id+1) '=' & [id+1+1) expr & [scope) where_clause
| [id) column & [id+1) '=' & [id+1+1) expr & [scope) on_using_condition
) & scope < id ->

The first highlighted condition specify that we are interested in columns, followed by equality, followed by some expression. They would be chosen together with the containing where_clause. Therefore, if we choose columns within the CASE scope

| [id) column & [id+1) '=' & [id+1+1) expr & [scope) case_expression

then it would align the equalities (but not THEN expressions):

SELECT
        CASE
            WHEN a.col5                = b.col5 THEN 'COL5'
            WHEN a.col55555555555555   = b.col55 THEN 'COL55555'
            WHEN a.col555555           = b.col55555 THEN 'COL555555'
        END
    AS val
FROM
    table1;

The condition we are after is a slight variation of thereof:

| [id) condition & [id+1) 'THEN' & [id+1+1) expr & [scope) case_expression

which produces:

SELECT
        CASE
            WHEN a.col5 = b.col5               THEN 'COL5'
            WHEN a.col55555555555555 = b.col55 THEN 'COL55555'
            WHEN a.col555555 = b.col55555      THEN 'COL555555'
        END
    AS val
FROM
    table1;

70 thoughts on “Custom Formatting in SQLDev 4.2

  1. Daniel Nyeste says:

    Hey Vladim,

    As the new SQLDeveloper is out, I try to bring together a custom format.
    I want to align the declaration items to default expressions to get from this:

    DECLARE

    tot_emps NUMBER;
    tot_depts NUMBER := 50;
    v_verylongresultname VARCHAR2(32767) := ”;
    v_val VARCHAR2(50) := ”;
    v_onechar VARCHAR2(1) := ‘I’;

    something like this:

    DECLARE

    tot_emps NUMBER;
    tot_depts NUMBER := 50;
    v_verylongresultname VARCHAR2(32767) := ”;
    v_val VARCHAR2(50) := ”;
    v_onechar VARCHAR2(1) := ‘I’;

    I tried to use this alignment code inside paddedIdsInScope, but does not work:

    | [id) constrained_type & [id+1) ‘:’ & [id+1+1) ‘=’ & [scope) decl_list

    In the code outline it shows constrained_type (or unconstrained_type) should be used as id there but I’m stuck. Any idea what’s going wrong here?

    Regards,
    Daniel Nyeste

    1. You want the next node at the same level:

      | [id) constrained_type & [id+1) default_expr_opt & [scope) decl_list

      Here is parse tree fragment:


      [28,37) basic_decl_item
      . [28,36) basic_d object_d
      . . [28,29) decl_id identifier
      . . [29,36) object_d_rhs
      . . . [29,33) constrained_type
      . . . . [29,30) 'VARCHAR2' unconstrained_...
      . . . . [30,33) constraint paren_expr_list
      . . . . . [30,31) '('
      . . . . . [31,32) and_expr arg arith_expr ...
      . . . . . [32,33) ')'
      . . . [33,36) default_expr_opt
      . . . . [33,34) ':'
      . . . . [34,35) '='
      . . . . [35,36) and_expr arith_expr boolean...
      . [36,37) ';'

      Output:

      DECLARE
          tot_emps               NUMBER;
          tot_depts              NUMBER                 := 50;
          v_verylongresultname   VARCHAR2(32767)        := '';
          v_val                  VARCHAR2(50)           := '';
          v_onechar              VARCHAR2(1)            := 'I';
      BEGIN
          NULL;
      END;
      
  2. Daniel Nyeste says:

    Hey Vadim,

    Thank you for your help despite the mistake I made in your name the first time. I’m really sorry for that.

    On the other hand, your answer works correctly 🙂

    Regards,
    Daniel

  3. I’m trying to deploy SQLcl as an auto-formatting solution for our team project, that is use it in a Git hook. I’m almost there, but I really wish Oracle would bundle `format.arbori` into code style XML. Because code style XML embeds an absolute path to `format.arbori`, it seems to me that I have to:

    1. Update code style XML and replace absolute path each time I export.
    2. cd/pushd in my script to tun sqlcl from a fixed directory to use this relative path.
    3. Do some shell programming to resolve relative file paths because of #2.

    Can you suggest any better options? If not, can embedding be channelled as a SQL Developer enhancement somewhere?

    1. The default formatting program — format.prg — is read from oracle.dbtools-common.jar (oracle/dbtools/app). If you replace it with your custom version, then you can disseminate the modified SQLcl (or just oracle.dbtools-common.jar) to the members of your team.

      P.S. I agree that formatter not recognizing relative path to the formatting arbori program is a bug.

      1. To be clear: it does recognize relative paths to format.arbori, but only relative to the current directory, which makes the whole thing clunky. I did finish a portable script that I could commit to a repo, but it’d have been easier to deal with a single code style XML.

        Thanks for your response!

  4. Piter says:

    I’m looking for a condition that creates Line Breaks after ‘(‘ . I want move subquery one line higher and ‘)’ on end subquery. Can you help me?

    1. One major shortcoming of 17.2 formatter is that there is no dedicated rule for line breaks. Line breaks are artifacts of indents, which limits what can be done with custom format. In 17.3 there is a separate rule for line breaks before and after a specified parse tree node. There I was able to achieve something sensible

      SELECT 1
      FROM dual
      WHERE
          1 = (
              SELECT 1 FROM dual
          )

      with “Line breaks->subqueries” option, and

      SELECT 1
      FROM dual
      WHERE
          1 = ( SELECT 1 FROM dual )

      without.

      P.S. this option looks broken in 17.2

  5. NP says:

    Hi Vadim,

    I l.ike to use leading comma in the column list i SELECT statement so I set “Line break” to be “Before Comma” in the Fomatting Options.

    Unfortunately this also makes some function calls harder to read.

    Example:

    — Unformatted
    SELECT M.MATERIAL
    , SUBSTR(M.MATERIAL, 1, INSTR(M.MATERIAL, ‘:’, -1, 1) – 1) MATERIAL_SHORT

    — After format:
    SELECT M.MATERIAL
    , SUBSTR(
    M.MATERIAL
    , 1
    , INSTR(
    M.MATERIAL
    , ‘:’
    , -1
    , 1
    ) – 1
    ) MATERIAL_SHORT

    Is there a way to exempt the commas within function calls from the Line Break rule?

    Thanks.

    NP

    1. This area has been improved for upcoming release 17.3 (due in couple of weeks at the end of the quarter). Here is default formatting:

      SELECT
          m.material,
          substr(m.material,1,instr(m.material,':',-1,1) - 1) material_short
      FROM
          dual

      And with leading commas:

      SELECT
          m.material
         ,substr(m.material,1,instr(m.material,':',-1,1) - 1) material_short
      FROM
          dual

      In 17.3 there are several notable improvements:
      1. Dedicated rules for line breaks
      2. A new advanced format option not to indent procedure arguments (default setting “no indent”)
      3. Dedicated rules to override excessive line breaks, so that, for example, line break is never inserted before semicolon.
      4. All advanced formatting options are “wired” into the custom format program so that they effectively turn on and off custom formatting rules, which results in more robust formatting job.

      All these amendments warrant a blog post .

  6. sage says:

    Please help me to get this format

    — create_package_body.htm#LNPLS01381
    CREATE OR REPLACE PACKAGE BODY emp_mgmt AS

    tot_emps NUMBER;
    tot_depts NUMBER;

    FUNCTION hire ( last_name VARCHAR2 ,
    job_id VARCHAR2 ,
    manager_id NUMBER ,
    salary NUMBER ,
    department_id NUMBER ) RETURN NUMBER IS
    new_empno NUMBER;
    BEGIN
    SELECT CASE “1”
    WHEN 1
    THEN ‘XX’
    END
    INTO new_empno
    FROM emp,
    dual d1,
    dual d2
    WHERE ( 1 = 2 OR 3 = 4 )
    AND 0 = 1 + 2
    AND EXISTS ( SELECT 1,
    2,
    3
    FROM wsh_new_deliveries wnd
    JOIN wsh_delivery_assignments wda ON wnd.delivery_id = wda.delivery_id
    JOIN hz_locations hl ON hps.location_id = hl.location_id
    );

    INSERT INTO employees
    VALUES ( new_empno,
    ‘First’ || ‘Middle’ || ‘Last’,
    ‘(415)555-0100′,
    TO_DATE(’18-JUN-2002′,’DD-MON-YYYY’),
    ‘IT_PROG’,
    90,
    100,
    110
    );

    tot_emps := tot_emps + 1; — := alignment
    out_rec.var_char1 := in_rec1.first_name;
    out_rec.var_char2222 := in_rec1.last_name;

    CASE /* PL/SQL CASE operator formal syntax is different from SQL CASE expression */
    WHEN 100 < salary
    THEN
    dbms_output.put_line('100 a1,– => alignment
    p11 => a1,
    p2 => a2
    );
    return(new_empno);
    END;

    END emp_mgmt;
    /

  7. Here is how to adjust the function header in 17.3:
    1. Remove indentation of the first parameter:

    -- | [node) prm_spec &       [node^) fml_part  

    2. Align second, third, and other parameters to the first one:

    pairwiseAlignments5: 
      [predecessor) prm_spec
    & [node) prm_spec  
    & ( 
         predecessor=node^-1 
       | predecessor=node^^-1 
       | predecessor=node^^^-1 
       | predecessor=node^^^^-1 
       )
    ; 
     
    pairwiseAlignments: 
        pairwiseAlignments1 
      | pairwiseAlignments2 
      | pairwiseAlignments3 
      | pairwiseAlignments4 
      | pairwiseAlignments5   
    ->;
    ;
    

    3. Remove linebreak before closed parenthesis:

    ignoreLineBreaksBeforeNode:
       !:breaksBeforeComma &    [node) ','
    |  [node) ';'
    | :breakAfterIf &  [node) pls_expr  &  [node-1) 'IF'
     | [node) ')' & [node^) fml_part    
    ;

    Result:

     
    CREATE OR REPLACE PACKAGE BODY emp_mgmt AS
    
        FUNCTION hire ( last_name       VARCHAR2,
                        job_id          VARCHAR2,
                        manager_id      NUMBER,
                        salary          NUMBER,
                        department_id   NUMBER ) RETURN NUMBER IS
            new_empno   NUMBER;
        BEGIN
            NULL;
        END;
    
    END emp_mgmt;
    /
  8. Arishtat says:

    Formatting for an expression list in an IN predicate inserts a trailing comma and a line break after each expression in an expression list.

    WHERE
    type IN (
    19,
    20,
    21
    )

    I wanted to get rid of the line breaks (also before the opening paren), but couldn’t figure out how to do it.

    WHERE
    type IN (19,20,21)

    Is this possible. I’m using 17.3.

  9. Ümit ŞAMLI says:

    Hi,
    Please help me to get this format.

    CURRENT :
    SELECT substr(
    s.text,1,500
    ),
    upper(
    s.text
    ),
    s.line
    || ‘ – ‘
    || s.text
    FROM dba_errors e,
    dba_source s
    WHERE s.name = e.name
    AND e.line = s.line
    ORDER BY e.name,
    e.sequence;

    EXPECTED:
    SELECT substr(s.text,1,500),
    upper(s.text),
    s.line|| ‘ – ‘ || s.text
    FROM dba_errors e,
    dba_source s
    WHERE s.name = e.name
    AND e.line = s.line
    ORDER BY e.name,
    e.sequence;

  10. Ilia Sazonov says:

    Hi Vadim,
    Version 17.3 has improved a lot in formatting – great job, guys!

    I’ve been struggling with :breaksAfterConcat. I see you added it into _extraBrkAfter section, which doesn’t implement indentation. How do I make concatenated nodes aligned?

    Also, if I have nested concatenations, breaksAfterConcat inserts a new line regardless of the level. For example,
    v.age_in_years || ‘ ‘ || nvl2( v.dob, ‘(‘ || to_char( v.dob, ‘mm/dd/yyyy’ ) || ‘)’, ‘ ‘ ) age_in_years
    gets transformed into

    v.age_in_years
    || ‘ ‘
    || nvl2( v.dob, ‘(‘ || to_char( v.dob, ‘mm/dd/yyyy’ )
    || ‘)’, ‘ ‘ ) age_in_years

    So I cheated and added the following lines into ignoreLineBreaksBeforeNode:
    | [node^^) “expr_list”
    | [node^^^) “expr_list”
    | [node^^^^) “expr_list”

    But I suspect, this is not an ideal solution.

    Could you advise, please?
    Thanks!

    1. The crux of the problem is what we consider desirable formatting for infix binary operator. Is

      1+2+3+(
        4+5+6
      )

      the most natural format? I would argue that binary operations should be represented in a way similar to parse tree:

         1
      +
         2
      +
         3
      +  (
            4
         +
            5
         +
            6
         )

      but don’t think any SQL developer would agree:-)

      Philosophy aside, here one possibility:

       -- amended _extraBrkAfter
      | :breaksBeforeConcat &  [node+1) '|'  & [node) '|'
      & ![node^^) "(x,y,z)" & ![node^^^) "(x,y,z)" & ![node^^^^) "(x,y,z)" -- added
      & ![node^^^^^) "(x,y,z)" & ![node^^^^^^) "(x,y,z)"                   -- added
      

      resulting in

      SELECT
          v.age_in_years
          || ' '
          || nvl2(v.dob,'(' || TO_CHAR(v.dob,'mm/dd/yyyy') || ')',' ') age_in_years
      FROM
          dual;

      And here is another:

      -- extra simpleIndentConditions
      | [node) "(x,y,z)"[5,27) & [node+10 < node) -- for long declarations only
      
      SELECT
          v.age_in_years
          || ' '
          || nvl2(
              v.dob,'('
              || TO_CHAR(v.dob,'mm/dd/yyyy')
              || ')',' '
          ) age_in_years
      FROM
          dual;
      1. Ilia Sazonov says:

        Thank you, Vadim. Both solutions worked well when select/from/where is checked in Advanced Format.

        However, if the box is unchecked, the formatting looks like this –

        #solution 1

        select v.patient_identifier
            , v.age_in_years
        || ' '
        || nvl2( v.dob, '(' || to_char( v.dob, 'mm/dd/yyyy' ) || ')', ' ' ) age_in_years
        

        Indentation didn’t work here.

        #solution 2

        select v.patient_identifier
            , v.age_in_years || ' ' || nvl2(
              v.dob, '(' || to_char( v.dob, 'mm/dd/yyyy' ) || ')', ' ' ) age_in_years
        

        Nested indentation didn’t work here.

        Guess I need to get used to a line break after select/from/where 🙂

      2. Align the concatenation symbols to preceeding expression:

        pairwiseAlignments1: predecessor = node-1 & (

        | [predecessor) expr & [node) compound_expression[25,44)
        );

        SELECT v.patient_identifier,
               v.age_in_years
               || ' '
               || nvl2(v.dob,'(' || TO_CHAR(v.dob,'mm/dd/yyyy') || ')',' ') age_in_years
        
  11. Ilia Sazonov says:

    Thank you, Vadim!

    One more question:

    
    select
        1
    from
        dual
    where
        1        = 1
        and 222222   = 222222
        and 3        = 3
    ;
    
    

    How do I align the first condition in the where clause?

  12. Stefan says:

    Hi Vadim!

    Firstly, thank you for all the articles. They’ve been great.
    I’d still however very much appreciate some help with the following example:

    https://pastebin.com/Lx0i4atq

    So far I added:
    | [node-1) ‘(‘ & [node) prm_spec
    to ignoreLineBreaksBeforeNode

    so that ‘(‘ and ‘last_name’ are on the same line and

    | [node) fml_part
    to _extraBrkBefore

    so that they are both on a new line.

    Now how could I align the ‘(‘, ‘,’ and ‘)’?

    And secondly, any way we can right-align a SELECT, FROM, WHERE in a query?

    Thanks again,
    Stefan

  13. Hi

    How to align the following
    PROCEDURE Tax_Val_Ptf (
    P_Nptf IN Ptf.Nptf%TYPE
    ,
    P_Noval IN NUMBER,
    P_Mnt_Div IN NUMBER,
    P_Carrondi IN NUMBER,
    P_Cdec IN NUMBER,
    P_Pct_Rctax IN OUT NUMBER,
    P_Mnt_Rctax IN OUT NUMBER,
    P_Pct_Untax IN OUT NUMBER,
    P_Mnt_Untax IN OUT NUMBER,
    P_Cretour IN OUT VARCHAR2,
    P_Nproc IN OUT VARCHAR2,
    P_Message IN OUT VARCHAR2,
    P_Cetat IN OUT VARCHAR2
    ) IS

    as

    PROCEDURE Tax_Val_Ptf (
    P_Nptf IN Ptf.Nptf%TYPE,
    P_Noval IN NUMBER,
    P_Mnt_Div IN NUMBER,
    P_Carrondi IN NUMBER,
    P_Cdec IN NUMBER,
    P_Pct_Rctax IN OUT NUMBER,
    P_Mnt_Rctax IN OUT NUMBER,
    P_Pct_Untax IN OUT NUMBER,
    P_Mnt_Untax IN OUT NUMBER,
    P_Cretour IN OUT VARCHAR2,
    P_Nproc IN OUT VARCHAR2,
    P_Message IN OUT VARCHAR2,
    P_Cetat IN OUT VARCHAR2
    ) IS

    If i have only IN and OUT then the alignment is ok using the property

    Line breaks on procedure arguments, if i have IN OUT, then the alignments are not OK

    Regards,
    Sumathi.V

    1. Kifezoj says:

      Hi Vadim, is it possible to slightly change this rule and add 3 spaces before lonely ‘OUT’? The result would be that “IN” and “OUT” will be in nice two optical columns.

      1. I agree that:

        CREATE PROCEDURE tax_val_ptf (
            p_nptf      IN      ptf.nptf%TYPE,
            p_noval     IN      NUMBER,
            p_mnt_div   IN      NUMBER,
            p_nproc        OUT  VARCHAR2,
            p_message   IN OUT  VARCHAR2,
            p_cetat     IN OUT  VARCHAR2
        ) IS
        BEGIN
        

        looks little better. However just adding three spaces won’t be enough. This OUT alignment makes sense only in the presence of IN OUT arguments.

      2. Kifezoj says:

        Thank you for your response. It’s really a pity that it can’t be done. Always three spaces before lonely OUT would be enough.

        Is it possible to at least ignore this line? (with lonely OUT) and not formatting it?

        I have changed these lines:

        | :alignTypeDecl & ( :breaksAfterComma | :breaksBeforeComma ) & [id) decl_id & ([id+1) prm_spec_unconstrained_type | [id+1) mode ) & [scope) fml_part
        | :alignTypeDecl & ( :breaksAfterComma | :breaksBeforeComma ) & [id) mode & [id+1) unconstrained_type & [scope) subprg_spec

        replaced
        [id+1) mode
        with
        ([id+1) mode & ![id+1) ‘OUT’)
        replaced [id) mode with
        ([id) mode & ![id) ‘OUT’)

        This worked, but line was formatted by basic style, despite the fact that I have added line here:
        dontFormatNode: [node) numeric_literal
        | [node) ‘OUT’

      3. Here is dontFormatNode rule amended to your spec (circa 19.1):

        numericLiteral: [node) numeric_literal
        ; 
        outParam: [node) prm_spec 
              & [outMode) 'OUT' 
              & outMode^ = node
        ;             
        dontFormatNode: numericLiteral | outParam  
        ->
        ;
      4. Kifezoj says:

        Thank you for your response.

        I just realized there is a mess when I try to format the
        IN OUT NOCOPY
        combination

        … do you have the same good advice?

      5. This case is still governed by the amended dontFormatNode rule, which is rather accidental. The parse tree branch:

                      [8,13)   prm_spec
                        [8,9)   decl_id  identifier
                        [9,10)   'IN'
                        [10,11)   'OUT'
                        [11,12)   'NOCOPY'
                        [12,13)   'NUMBER'  pls_number_datatypes  unconstrained_type  unconstrained_type_wo_datetime
        

        is wrong, as there should be intermediate tree node labeled “mode”. I suggest modifying this line:

        | :alignTypeDecl & ( :breaksAfterComma | :breaksBeforeComma ) & [id) decl_id & ([id+1) prm_spec_unconstrained_type | [id+1) mode /*parse bug workawound:*/| [id+1) 'OUT' | [id+1) 'IN' ) & [scope) fml_part
        CREATE PROCEDURE proc_name (
            param1             OUT     NUMBER,
            param2             IN OUT NOCOPY NUMBER,
            param22            IN OUT  NUMBER,
            param3             IN      VARCHAR2,
            param4             IN      VARCHAR2,
            p_table_name       VARCHAR2,
            p_transaction_id   NUMBER,
            p_calendar_code    VARCHAR2,
            p_message_text     VARCHAR2,
            p_column_names     VARCHAR2,
            p_error_text       OUT NOCOPY VARCHAR2
        ) AS
        BEGIN
            NULL;
        END;
      6. Kifezoj says:

        Thank you for your response. I have also modified this rule:

        | :alignTypeDecl & ( :breaksAfterComma | :breaksBeforeComma ) & [id) mode & [id+1) unconstrained_type & [scope) subprg_spec

        … adding NOCOPY element like this:

        | :alignTypeDecl & ( :breaksAfterComma | :breaksBeforeComma ) & (([id) mode & [id+1) unconstrained_type) | ([id) mode & [id+1) ‘NOCOPY’ & [id+2) unconstrained_type)) & [scope) subprg_spec

        but it does not work. Do you have any good advice for me?

      7. The syntax for the grammar rule involving NOCOPY is inconsistent– check the parse tree fragment from my previous reply. It should be the mode followed up by NOCOPY, but instead it is either IN OUT or OUT which is not recognized as a mode.

  14. Hi

    I have few more queries

    1st question
    In the below code how to align the datatypes DATE, VARCHAR2, NUMBER etc.,

    PROCEDURE Calc_Next_Dep(
    P_Ddebut IN DATE,
    P_Dfin IN DATE,
    P_Cusance IN VARCHAR2,
    P_New_Ddebut OUT DATE,
    P_New_Dfin OUT DATE,
    P_Cretour IN OUT VARCHAR2,
    P_Nproc IN OUT VARCHAR2,
    P_Message IN OUT VARCHAR2,
    P_Cetat IN OUT VARCHAR2
    ) IS

    2nd question

    How to align the below cursor code as

    CURSOR Cur_Tax IS SELECT Nvl(Pct_Un,0) Pct_Un,
    Nvl(Pct_Rc,0) Pct_Rc
    FROM T_Taxval
    WHERE Nptf = P_Nptf
    AND Noval = P_Noval;

    as
    CURSOR Cur_Tax IS
    SELECT Nvl(Pct_Un,0) Pct_Un,
    Nvl(Pct_Rc,0) Pct_Rc
    FROM T_Taxval
    WHERE Nptf = P_Nptf
    AND Noval = P_Noval;

    i.e., right align the keywords CURSOR, SELECT, FROM, WHERE AND

    Thanks in advance

    Regards,
    Sumathi.V

    1. Add one more padding condition:
      | :alignTypeDecl & ( :breaksAfterComma | :breaksBeforeComma ) & [id) mode & [id+1) unconstrained_type & [scope) fml_part

      Keyword right alignment option is there on Advanced Format panel since 18.1

  15. Parker says:

    Hi Vadim,

    I’m new to playing with grammar like this and I was hoping you could help me out a bit.

    Currently my code formats like

    SELECT a.col_1
    ,b.col_2
    FROM tab_a a
    ,tab_b b

    And my desired output is

    SELECT a.col_1
    ,b.col_2
    FROM tab_a a
    ,tab_b b

    I would like to carry this style through the WHERE clause as well but I’m trying to take it one step at a time (I did get the SELECT statement on my own!) I’ve tried doing
    [node) ‘,’ & [node+1) table_expression,
    [node) ‘,’ & [node+1) table_reference_or_join_clause, etc. But haven’t been able to get anything to work.

    Would you be able to tell me how to achieve my desired output and tell me how you were able to figure it out? Any help is much appreciated!

    1. Parker says:

      Sorry Vadim, didn’t realize my formatting would get modified. Using periods for spaces.

      Current format

      SELECT a.col_1
      …………..,b.col_2
      ….FROM tab_a a
      ……,tab_b b

      Desired format
      SELECT a.col_1
      …………..,b.col_2
      ….FROM tab_a a
      …………..,tab_b b

      1. Your modified settings seems to be
        alignRight (keywords) = true
        breaksAfterSelect = false
        breaksComma = before
        You correct, the second line in the FROM clause is misaligned, which is a bug. Fixed for 18.3.

        The fix/workaround: relocate the whole rightAlignments rule above paddedIdsInScope. This would make right alignments performed before aligning the table references.

      2. Parker says:

        Thanks Vadim, however this did not work as intended, it ended up left aligning my SQL but did align my tables and columns. I had another question I was hoping you could answer. In SQL Developer we are able to see the arbori through the custom format. I noticed each section was actually an arbori program. So my question is, can you tell me how the arbori programs are read by SQL Developer? For example, our first is simpleIndentConditions, what is running this program and how is it being used to indent the SQL?

      3. @Parker
        The parser, Arbori query engine, and their numerous applications, such as the formatter are the part of the dbtools-common library. This library has been briefly featured at GitHub, but was taken away because it exposed some proprietary code. Consequently, I had to take down couple of blog articles which have described how to use parser and arbori in your Java application. Still the management plans to eventually publish this library, so satisfactory answer to your question would be just pointing out to Java code that does it. For now, I could only hand wave, but let’s try. Each arbori rule calls java function with the same name via Java reflection, and those functions build the appropriate data structures, such as the map of all line breaks.

  16. Hi

    I have the following query, i have the following statement
    IF P_Frequency NOT IN ( ‘0001’,’1001′,’0002′,’0003′,’0004′,’0005′,’0006′,’0007′,’0008′,’0009′,’0010′,’0011′,’0012′,’0013′,’0014′,’0015′,’0016′,’0017′,’0018′,’0019′,’0022′,’0023′
    ) THEN
    RAISE Wrong_Frequency;
    END IF;

    I want to align as follows

    IF P_Frequency NOT IN (‘0001′,’1001′,’0002′,’0003′,’0004′,’0005’,
    ‘0006’,’0007′,’0008′,’0009′,’0010′,’0011′,’0012′,’0013′,’0014′,’0015′,’0016′,’0017′,’0018′,’0019′,’0022′,’0023′) THEN
    RAISE Wrong_Frequency;
    END IF;

    i.e. no line break after ‘IN (‘ or ‘NOT IN (‘ and also after the closing parenthsis ‘)’

    Thanks in advance

    Regards,
    Sumathi.V

    1. I think it worked, i have added the below which is marked as ‘– added this line

      ignoreLineBreaksBeforeNode:
      !:breaksBeforeComma & [node) ‘,’
      | [node) ‘;’
      | :breakAfterIf & [node) pls_expr & [node-1) ‘IF’
      | [node) ‘)’ — added this line
      ->
      ;

      ignoreLineBreaksAfterNode:
      !:breaksAfterComma & [node) ‘,’
      | [node) ‘,’ & [node-1) “expr_list”
      | :breakAfterIf & [node) pls_expr & [node-1) ‘IF’
      | [node) ‘(‘ — added this line
      ->
      ;

      Not sure whether the solution is correct or not

      Regards,
      Sumathi.V

  17. balakrishna says:

    Hi
    How to give line break between IS and SELECT in the below cursor statement

    CURSOR Cur_Tax IS SELECT Nvl(Pct_Un,0) Pct_Un,
    Nvl(Pct_Rc,0) Pct_Rc
    FROM T_Taxval
    WHERE Nptf = P_Nptf
    AND Noval = P_Noval;

    Now using version 18.1

    Regards,
    Sumathi.V

      1. balakrishna says:

        I have added only | [node-1) cursor_d it worked, when i added the above as suggested, it was giving error

        Regards,
        Sumathi.V

  18. balakrishna says:

    Hi,

    In fact i have three queries related to cursors

    In the below cursor

    CURSOR Cur_Details ( Vc_Ntransac T_Re_Trns_Mst.Ntransac%TYPE,
    Vc_Noutlet T_Re_Trns_Mst.Noutlet%TYPE ) IS SELECT Nregister,
    Proxy,
    Nprod,
    Nprod_Transf,
    Ntfc,
    Ntfc_Transf
    FROM T_Re_Trns_Mst
    WHERE Ntransac = Vc_Ntransac
    AND Noutlet = Vc_Noutlet
    AND Coperation IN ( ‘0018’,’0020′,’0033′,’0082′ );

    1. Want to line break between IS and SELECT
    2. Want to right align corners CURSOR, SELECT, FROM, WHERE, AND (if right align option of 18.1 is used it aligns only SELECT, FROM and WHERE)
    3. The cursor variable VC_NTRANSAC, VC_NOUTLET is to be aligned one below the other (similar to the procedure agrugments)

    Regards,
    Sumathi.V

    1. balakrishna says:

      Hi

      When i added | [node-1) cursor_d in the _extraBrkAfter rule, all the above issues got solved.

      Thanks,
      Sumathi.V

  19. balakrishna says:

    Hi,

    I have the following query, currently i have a cursor as follows

    Cursor C_Min_Limit_All Is
    Select Type_Id_Code, Id_Code, Nptf, Tpart, Cmon
    From T_Ta_Investment_Limit
    Where Type_Id_Code = Vc_Type_Id_Code
    And Id_Code = Vc_Id_Code
    Order By Decode(Tpart,’@@@@’,1,0);

    i want to retain the select statement section below

    Select Type_Id_Code, Id_Code, Nptf, Tpart, Cmon

    as it is , since i have opted for break after comma, the select list is getting re-aligned as follows

    Select Type_Id_Code,
    Id_Code,
    Nptf,
    Tpart,
    Cmon

    Is there any way to handle this.

    Regards,
    Sumathi.V

    1. 1. Disable indentation of select_list which is controlled via condition
      | :breaksAfterSelectFromWhere & [node) select_list & [node^) select_clause
      This can be done either with flipping the corresponding option at Advanced Format settings or editing this line at the Custom Format page
      2. Disable breaks after comma which is controlled in two places
      | :breaksAfterComma & [node+1) select_term & [node) ‘,’

      | :breaksAfterComma & ([node+1) expr | [node+1) column) & [node) ‘,’
      & ![node^) aggregate_function & ![node^) analytic_function

      This condition, again, can be disabled either from advanced format page, or explicitly in the arbori code. Since you have mentioned that you don’t want to set line breaks on commas to None, then just comment these lines at the Custom Format page

      These exact formatting conditions are described in more recent article

      1. balakrishna says:

        Hi,

        The above changes is bringing all the select list columns in single line.

        Actually my question was

        I have the following query, currently i have a cursor as follows

        Cursor C_Min_Limit_All Is
        Select Type_Id_Code, Id_Code,
        Nptf, Tpart, Cmon
        From T_Ta_Investment_Limit
        Where Type_Id_Code = Vc_Type_Id_Code
        And Id_Code = Vc_Id_Code
        Order By Decode(Tpart,’@@@@’,1,0);

        i want to retain the select statement section as it is how the developer had typed (i.e., no comma breaks for select_list)

        Select Type_Id_Code, Id_Code,
        Nptf, Tpart, Cmon

        Regards,
        Sumathi.V

      2. There is no method to preserve a section of the code such as select_clause. The only option for “partial formatting” sqldev offers is “Convert case only” at the basic format page. There is also another option, which might help – “Max char line width”. Admittedly, it is the least intelligent of the formatting features, as it would just wrap a long line exceeding some character length limit.

  20. Dilli says:

    I’m using Sql Developer Ver 19.2.1.247 and I’ve the following statement, where the in_condition need to be aligned in single line

    SELECT a
    ,b
    ,c
    FROM perks_basket_purchase
    WHERE str_no IN(
    8148
    )
    AND tx_dte_tme BETWEEN TO_DATE(‘20190802′,’YYYYMMDD’)AND TO_DATE(‘20190831′,’YYYYMMDD’)
    AND trml_no = 0
    AND tx_no = 0;

    I want to align as follows

    SELECT a
    ,b
    ,c
    FROM table1
    WHERE str_no IN (0)
    AND tx_dte_tme BETWEEN TO_DATE ( ‘20190802’, ‘YYYYMMDD’) AND TO_DATE ( ‘20190831’, ‘YYYYMMDD’)
    AND trml_no = 0
    AND tx_no = 0;

    Also is there is way to automatically generate tagline with header comments to show when the code was Formatted on
    /* Formatted on 12/9/2019 11:17:20 AM */
    SELECT a
    ,b
    ,c
    FROM table1
    WHERE str_no IN (0)
    AND tx_dte_tme BETWEEN TO_DATE ( ‘20190802’, ‘YYYYMMDD’) AND TO_DATE ( ‘20190831’, ‘YYYYMMDD’)
    AND trml_no = 0
    AND tx_no = 0;

    Thanks,
    Dilly

    1. | .breaksProcArgs & [node) “expr_list” & [node^) “(x,y,z)” & ![node) compound_expression & ![node) identifier
      –| [node) expression_list & ![node) grouping_expression_list
      | [node) external_table_data_props

      | :breaksBeforeComma & [node^) group_by_list[5,12) & [node) ‘,’
      –| :breaksBeforeComma & [node+1) expr & [node) ‘,’
      | :breaksBeforeComma & [node+1) column & [node) ‘,’

      — additional rule:
      timestamp: runOnce -> {
      var date = new java.util.Date();
      struct.putNewline(0, “/* Formatted on “+date+” */\n”);
      }

      —- Output —–

      /* Formatted on Mon Dec 09 11:56:55 PST 2019 */
      SELECT
          a
        , b
        , c
      FROM
          table1
      WHERE
          str_no IN ( 0 )
          AND str_no IN ( 0, 1 );
      
      1. Dilly says:

        Thanks Vadim for your reply.

        — additional rule:
        timestamp: runOnce -> {
        var date = new java.util.Date();
        struct.putNewline(0, “/* Formatted on “+date+” */\n”);
        }

        Your code helped to automatically create the header comment, however it append the header comment when the code is formatted multiple times.
        /* Formatted on Mon Jan 06 11:57:42 EST 2020 *//* Formatted on Mon Jan 06 11:57:44 EST 2020 */

        Is is possible to override the header comment, when the code is formatted multiple times.
        /* Formatted on Mon Jan 06 11:57:44 EST 2020 */

        Thanks,
        Dilly

      2. Not possible in 19.4. I have amended the code for 20.1. The timestamp rule is the last, but is disabled by default like this:

        timestamp: runOnce & false -> {

        Just remove or comment the “false” literal in order to enable it.

      3. Dilly says:

        If the header comment can’t be override in 19.4, is it possible to remove the first line of the code, if the code contain the string %Formatted on%

      4. Technically, the formatting core functionality is written in Java and, therefore, can’t be hacked. In 19.4 the input text is not accessible to the Nashorn scripting that I’m using in the custom format program. I fixed it for 20.1.

      5. Dilly says:

        Thanks Vadim for your code on the header comment.

        We are migrating from Toad to Sql developer and below are few requirement to format the code on the Sql developer.
        I would really appreciate, if you could help me out.

        1) Line break after the hint on the Select
        SELECT /*+ use_hash(rtd st incl str) */ rtd.str_no AS str_no
        ,TRUNC (rtd.tx_dte_tme) AS tx_dte
        ,NVL (st.trml_typ_cd,’UNK’) AS trml_typ_cd

        Expected:
        SELECT /*+ use_hash(rtd st incl str) */
        rtd.str_no AS str_no
        ,TRUNC ( rtd.tx_dte_tme) AS tx_dte
        ,NVL (st.trml_typ_cd, ‘UNK’) AS trml_typ_cd

        2) Extra line break before Cursor declaration only when variable/constant defined above.
        c_defaultBsStrNo CONSTANT STORE.str_no%TYPE := 0;
        CURSOR l_rtlTndrDtlCur IS
        SELECT * from dual

        Expected
        c_defaultBsStrNo CONSTANT STORE.str_no%TYPE := 0;

        CURSOR l_rtlTndrDtlCur IS
        SELECT * from dual

        3) Remove line beak on Procedure / Function / Cursor parameter declaration. Line break are need only when the declaration exceeds MAX CHAR LINE WIDTH
        PROCEDURE insTndrOnly (
        p_runDte IN DATE
        ,p_txDte IN DATE
        );

        Expected :
        PROCEDURE insTndrOnly (p_runDte IN DATE, p_txDte IN DATE);

        4) When invoking the procedure, line breaksBeforeComma on each parameter and also parenthesis need to be aligned

        SELECT a
        ,b
        FROM TABLE (transformRTDForInsOfOneActnCd (p_runDte,p_txDte,c_tndrActnCd,c_crctnActnCd,c_chgActnCd));

        Expected:
        SELECT a
        ,b
        FROM TABLE (transformRTDForInsOfOneActnCd (p_runDte
        ,p_txDte
        ,c_tndrActnCd
        ,c_crctnActnCd
        ,c_chgActnCd
        )
        );

        5) if_stmt – Line break before on the and_expr.
        IF (l_prevStrNo = l_strNo) AND (l_prevTrmlNo = l_trmlNo) AND (l_prevTxNo = l_txNo) AND (l_prevTxDte = l_txDte) AND (l_prevTrmlTypCd = l_trmlTypCd) AND (l_prevRunDTe = l_runDte

        Expected:
        IF (l_prevStrNo = l_strNo)
        AND (l_prevTrmlNo = l_trmlNo)
        AND (l_prevTxNo = l_txNo)
        AND (l_prevTxDte = l_txDte)
        AND (l_prevTrmlTypCd = l_trmlTypCd)
        AND (l_prevRunDTe = l_runDte)

        Thanks in Advance

      6. Embedding code in the wordpress comment section is nontrivial. Therefore, can you please post your requirements on stackoverflow (under the tag oracle-sqldeveloper) or the OTN forum? Please also be specific what Advanced Format options you have changed, and give little more context to the code snippets (for example, the IF statement in your last case, was it from SQL or PL/SQL?)

Leave a reply to Daniel Nyeste Cancel reply