From: Gilles Darold Date: Wed, 12 Sep 2018 19:16:18 +0000 (+0200) Subject: Update SQL beautifier to v3.1 of pgFormatter. X-Git-Tag: v10.1~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0ab5c8d90046341d8eb03ff8c457f1559ade0bb9;p=pgbadger Update SQL beautifier to v3.1 of pgFormatter. --- diff --git a/pgbadger b/pgbadger index 71b0114..4480cb1 100755 --- a/pgbadger +++ b/pgbadger @@ -14954,7 +14954,6 @@ sub change_timezone # Inclusion of Perl package pgFormatter::Beautify. { - package pgFormatter::Beautify; use strict; @@ -14965,6 +14964,16 @@ use Encode qw( decode ); # PostgreSQL functions that use a FROM clause our @have_from_clause = qw( extract overlay substring trim ); +=head1 NAME + +pgFormatter::Beautify - Library for pretty-printing SQL queries + +=head1 VERSION + +Version 3.0 + +=cut + # Version of pgFormatter our $VERSION = '3.0'; @@ -14972,6 +14981,107 @@ our $VERSION = '3.0'; # Copyright (C) 2009 by Jonas Kramer # Published under the terms of the Artistic License 2.0. +=head1 SYNOPSIS + +This module can be used to reformat given SQL query, optionally anonymizing parameters. + +Output can be either plain text, or it can be HTML with appropriate styles so that it can be displayed on a web page. + +Example usage: + + my $beautifier = pgFormatter::Beautify->new(); + $beautifier->query( 'select a,b,c from d where e = f' ); + + $beautifier->beautify(); + my $nice_txt = $beautifier->content(); + + $beautifier->format('html'); + $beautifier->beautify(); + my $nice_html = $beautifier->content(); + + $beautifier->format('html'); + $beautifier->anonymize(); + $beautifier->beautify(); + my $nice_anonymized_html = $beautifier->content(); + +=head1 FUNCTIONS + +=head2 new + +Generic constructor - creates object, sets defaults, and reads config from given hash with options. + +Takes options as hash. Following options are recognized: + +=over + +=item * break - String that is used for linebreaks. Default is "\n". + +=item * colorize - if set to false CSS style will not be applied to html output. Used internally to display errors in CGI mode withour style. + +=item * comma - set comma at beginning or end of a line in a parameter list + +=over + +=item end - put comma at end of the list (default) + +=item start - put comma at beginning of the list + +=back + +=item * comma_break - add new-line after each comma in INSERT statements + +=item * format - set beautify format to apply to the content (default: text) + +=over + +=item text - output content as plain/text (command line mode default) + +=item html - output text/html with CSS style applied to content (CGI mode default) + +=back + +=item * functions - list (arrayref) of strings that are function names + +=item * keywords - list (arrayref) of strings that are keywords + +=item * no_comments - if set to true comments will be removed from query + +=item * placeholder - use the specified regex to find code that must not be changed in the query. + +=item * query - query to beautify + +=item * rules - hash of rules - uses rule semantics from SQL::Beautify + +=item * space - character(s) to be used as space for indentation + +=item * spaces - how many spaces to use for indentation + +=item * uc_functions - what to do with function names: + +=over + +=item 0 - do not change + +=item 1 - change to lower case + +=item 2 - change to upper case + +=item 3 - change to Capitalized + +=back + +=item * separator - string used as dynamic code separator, default is single quote. + +=item * uc_keywords - what to do with keywords - meaning of value like with uc_functions + +=item * wrap - wraps given keywords in pre- and post- markup. Specific docs in SQL::Beautify + +=back + +For defaults, please check function L. + +=cut + sub new { my $class = shift; my %options = @_; @@ -15008,15 +15118,63 @@ sub new { return $self; } +=head2 query + +Accessor to query string. Both reads: + + $object->query() + +, and writes + + $object->query( $something ) + +=cut + sub query { my $self = shift; my $new_value = shift; $self->{ 'query' } = $new_value if defined $new_value; + my $i = 0; + my %temp_placeholder = (); + my @temp_content = split(/(CREATE(?:\s+OR\s+REPLACE)?\s+(?:FUNCTION|PROCEDURE)\s+)/i, $self->{ 'query' }); + if ($#temp_content > 0) { + for (my $j = 0; $j <= $#temp_content; $j++) { + next if ($temp_content[$j] =~ /^CREATE/i); + my $fctname = ''; + if ($temp_content[$j] =~ /^([^\s\(]+)/) { + $fctname = lc($1); + } + next if (!$fctname); + my $language = 'sql'; + if ($temp_content[$j] =~ /\s+LANGUAGE\s+[']*([^'\s;]+)[']*/i) { + $language = lc($1); + } + # if the function language is not SQL or PLPGSQL + if ($language !~ /^(?:plpg)sql$/) { + # Try to find the code separator + my $tmp_str = $temp_content[$j]; + while ($tmp_str =~ s/\s+AS\s+([^\s]+)\s+//is) { + my $code_sep = quotemeta($1); + foreach my $k (@{ $self->{ 'keywords' } }) { + last if ($code_sep =~ s/\b$k$//i); + } + if ($tmp_str =~ /\s+$code_sep[\s;]+/) { + while ( $temp_content[$j] =~ s/($code_sep.*$code_sep)/CODEPART${i}CODEPART/s) { + push(@{ $self->{ 'placeholder_values' } }, $1); + $i++; + } + last; + } + } + } + } + } + $self->{ 'query' } = join('', @temp_content); + # Store values of code that must not be changed following the given placeholder if ($self->{ 'placeholder' }) { - my $i = 0; while ( $self->{ 'query' } =~ s/($self->{ 'placeholder' })/PLACEHOLDER${i}PLACEHOLDER/) { push(@{ $self->{ 'placeholder_values' } }, $1); $i++; @@ -15029,6 +15187,15 @@ sub query { return $self->{ 'query' }; } +=head2 content + +Accessor to content of results. Must be called after $object->beautify(). + +This can be either plain text or html following the format asked by the +client with the $object->format() method. + +=cut + sub content { my $self = shift; my $new_value = shift; @@ -15045,13 +15212,21 @@ sub content { #$self->_restore_comments(\$self->{ 'content' }); # Replace placeholders by their original values - if ($self->{ 'placeholder' }) { + if ($#{ $self->{ 'placeholder_values' } } >= 0) + { $self->{ 'content' } =~ s/PLACEHOLDER(\d+)PLACEHOLDER/$self->{ 'placeholder_values' }[$1]/igs; + $self->{ 'content' } =~ s/CODEPART(\d+)CODEPART/$self->{ 'placeholder_values' }[$1]/igs; } return $self->{ 'content' }; } +=head2 highlight_code + +Makes result html with styles set for highlighting. + +=cut + sub highlight_code { my ($self, $token, $last_token, $next_token) = @_; @@ -15063,62 +15238,62 @@ sub highlight_code { while ( my ( $k, $v ) = each %{ $self->{ 'dict' }->{ 'symbols' } } ) { if ($token eq $k) { $token = '' . $v . ''; - return $token; - } + return $token; + } } # lowercase/uppercase keywords if ( $self->_is_keyword( $token ) ) { - if ( $self->{ 'uc_keywords' } == 1 ) { - $token = '' . $token . ''; - } elsif ( $self->{ 'uc_keywords' } == 2 ) { - $token = '' . $token . ''; - } elsif ( $self->{ 'uc_keywords' } == 3 ) { - $token = '' . $token . ''; - } else { - $token = '' . $token . ''; - } - return $token; + if ( $self->{ 'uc_keywords' } == 1 ) { + $token = '' . $token . ''; + } elsif ( $self->{ 'uc_keywords' } == 2 ) { + $token = '' . $token . ''; + } elsif ( $self->{ 'uc_keywords' } == 3 ) { + $token = '' . $token . ''; + } else { + $token = '' . $token . ''; + } + return $token; } # lowercase/uppercase known functions or words followed by an open parenthesis if the token is not an open parenthesis if ($self->_is_function( $token ) || (!$self->_is_keyword( $token ) && $next_token eq '(' && $token ne '(' && !$self->_is_comment( $token )) ) { - if ($self->{ 'uc_functions' } == 1) { - $token = '' . $token . ''; - } elsif ($self->{ 'uc_functions' } == 2) { - $token = '' . $token . ''; - } elsif ($self->{ 'uc_functions' } == 3) { - $token = '' . $token . ''; - } else { - $token = '' . $token . ''; - } - return $token; + if ($self->{ 'uc_functions' } == 1) { + $token = '' . $token . ''; + } elsif ($self->{ 'uc_functions' } == 2) { + $token = '' . $token . ''; + } elsif ($self->{ 'uc_functions' } == 3) { + $token = '' . $token . ''; + } else { + $token = '' . $token . ''; + } + return $token; } # Colorize STDIN/STDOUT in COPY statement if ( grep(/^\Q$token\E$/i, @{ $self->{ 'dict' }->{ 'copy_keywords' } }) ) { - if ($self->{ 'uc_keywords' } == 1) { - $token = '' . $token . ''; - } elsif ($self->{ 'uc_keywords' } == 2) { - $token = '' . $token . ''; - } elsif ($self->{ 'uc_keywords' } == 3) { - $token = '' . $token . ''; - } else { - $token = '' . $token . ''; - } - return $token; + if ($self->{ 'uc_keywords' } == 1) { + $token = '' . $token . ''; + } elsif ($self->{ 'uc_keywords' } == 2) { + $token = '' . $token . ''; + } elsif ($self->{ 'uc_keywords' } == 3) { + $token = '' . $token . ''; + } else { + $token = '' . $token . ''; + } + return $token; } # Colorize parenthesis if ( grep(/^\Q$token\E$/i, @{ $self->{ 'dict' }->{ 'brackets' } }) ) { $token = '' . $token . ''; - return $token; + return $token; } # Colorize comment if ( $self->_is_comment( $token ) ) { $token = '' . $token . ''; - return $token; + return $token; } # Colorize numbers @@ -15131,6 +15306,14 @@ sub highlight_code { return $token; } +=head2 tokenize_sql + +Splits input SQL into tokens + +Code lifted from SQL::Beautify + +=cut + sub tokenize_sql { my $self = shift; my $query = $self->query(); @@ -15203,6 +15386,16 @@ sub tokenize_sql { return @query; } +=head2 beautify + +Beautify SQL. + +After calling this function, $object->content() will contain nicely indented result. + +Code lifted from SQL::Beautify + +=cut + sub beautify { my $self = shift; @@ -15242,21 +15435,21 @@ sub beautify { while ( defined( my $token = $self->_token ) ) { my $rule = $self->_get_rule( $token ); - #### - # Find if the current keyword is a known function name - #### + #### + # Find if the current keyword is a known function name + #### if (defined $last && $last && defined $self->_next_token and $self->_next_token eq '(') { - my $word = $token; - $word =~ s/^[^\.]+\.//; - if ($word && grep(/^\Q$word\E$/i, @{$self->{ 'dict' }->{ 'pg_functions' }})) { - $self->{ '_is_in_function' }++; - } + my $word = $token; + $word =~ s/^[^\.]+\.//; + if ($word && grep(/^\Q$word\E$/i, @{$self->{ 'dict' }->{ 'pg_functions' }})) { + $self->{ '_is_in_function' }++; + } } - #### + #### # Set open parenthesis position to know if we - # are in subqueries or function parameters - #### + # are in subqueries or function parameters + #### if ( $token eq ')') { if (!$self->{ '_is_in_function' }) { @@ -15278,71 +15471,74 @@ sub beautify { } } - #### - # Control case where we have to add a newline, go back and - # reset indentation after the last ) in the WITH statement - #### - if ($token =~ /^WITH$/i && (!defined $last || $last ne ')')) { + #### + # Control case where we have to add a newline, go back and + # reset indentation after the last ) in the WITH statement + #### + if ($token =~ /^WITH$/i && (!defined $last || $last ne ')')) + { $self->{ '_is_in_with' } = 1; } - elsif ($token =~ /^(AS|IS)$/i && defined $self->_next_token && $self->_next_token eq '(') { + elsif ($token =~ /^(AS|IS)$/i && defined $self->_next_token && $self->_next_token eq '(') + { $self->{ '_is_in_with' }++ if ($self->{ '_is_in_with' } == 1); } - elsif ( $token eq ')' ) { - $self->{ '_has_order_by' } = 0; + elsif ( $token eq ')' ) + { + $self->{ '_has_order_by' } = 0; $self->{ '_has_from' } = 0; if ($self->{ '_is_in_with' } > 1 && !$self->{ '_parenthesis_level' }) { - $self->_new_line; + $self->_new_line; $self->_back; $self->_add_token( $token ); @{ $self->{ '_level_stack' } } = (); $self->{ '_level' } = 0; $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - if ($self->{ '_is_in_with' }) { - if (defined $self->_next_token && $self->_next_token eq ',') { - $self->{ '_is_in_with' } = 1; - } else { - $self->{ '_is_in_with' } = 0; - } - } - next; + if ($self->{ '_is_in_with' }) { + if (defined $self->_next_token && $self->_next_token eq ',') { + $self->{ '_is_in_with' } = 1; + } else { + $self->{ '_is_in_with' } = 0; + } + } + next; } - } + } - #### - # Set the current kind of statement parsed - #### + #### + # Set the current kind of statement parsed + #### if ($token =~ /^(FUNCTION|PROCEDURE|SEQUENCE|INSERT|DELETE|UPDATE|SELECT|RAISE|ALTER|GRANT|REVOKE)$/i) { - my $k_stmt = uc($1); - # Set current statement with taking care to exclude of SELECT ... FOR UPDATE statement. + my $k_stmt = uc($1); + # Set current statement with taking care to exclude of SELECT ... FOR UPDATE statement. if ($k_stmt ne 'UPDATE' or (defined $self->_next_token and $self->_next_token ne ';' and $self->_next_token ne ')')) { $self->{ '_current_sql_stmt' } = $k_stmt if ($self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/i); - } + } } - #### - # Mark that we are in CREATE statement that need newline - # after a comma in the parameter, declare or column lists. - #### + #### + # Mark that we are in CREATE statement that need newline + # after a comma in the parameter, declare or column lists. + #### if ($token =~ /^CREATE$/i && $self->_next_token !~ /^(UNIQUE|INDEX|EXTENSION|TYPE)$/i) { $self->{ '_is_in_create' } = 1; } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^TYPE$/i) { $self->{ '_is_in_type' } = 1; } - #### - # Mark that we are in index/constraint creation statement to - # avoid inserting a newline after comma and AND/OR keywords. + #### + # Mark that we are in index/constraint creation statement to + # avoid inserting a newline after comma and AND/OR keywords. # This also used in SET statement taking care that we are not - # in update statement. CREATE statement are not subject to this rule - #### + # in update statement. CREATE statement are not subject to this rule + #### if (! $self->{ '_is_in_create' } and $token =~ /^(INDEX|PRIMARY|CONSTRAINT)$/i) { $self->{ '_is_in_index' } = 1; } elsif (! $self->{ '_is_in_create' } and uc($token) eq 'SET') { $self->{ '_is_in_index' } = 1 if ($self->{ '_current_sql_stmt' } ne 'UPDATE'); } # Same as above but for ALTER FUNCTION/PROCEDURE/SEQUENCE or when - # we are in a CREATE FUNCTION/PROCEDURE statement + # we are in a CREATE FUNCTION/PROCEDURE statement elsif ($token =~ /^(FUNCTION|PROCEDURE|SEQUENCE)$/i) { $self->{ '_is_in_index' } = 1 if (uc($last) eq 'ALTER'); if ($token =~ /^(FUNCTION|PROCEDURE)$/i && $self->{ '_is_in_create' }) { @@ -15352,56 +15548,70 @@ sub beautify { # Desactivate index like formatting when RETURN(S) keyword is found elsif ($token =~ /^(RETURN|RETURNS)$/i) { $self->{ '_is_in_index' } = 0; - } - - #### - # Mark statements that use string_agg() or group_concat() function - # as statement that can have an ORDER BY clause inside the call to - # prevent applying order by formatting. - #### - if ($token =~ /^(string_agg|group_concat)$/i) { - $self->{ '_has_order_by' } = 1; + } + + #### + # Mark statements that use string_agg() or group_concat() function + # as statement that can have an ORDER BY clause inside the call to + # prevent applying order by formatting. + #### + if ($token =~ /^(string_agg|group_concat)$/i) { + $self->{ '_has_order_by' } = 1; } elsif ( $self->{ '_has_order_by' } and uc($token) eq 'ORDER' and $self->_next_token =~ /^BY$/i) { - $self->_add_token( $token ); + $self->_add_token( $token ); $last = $token; next; } elsif ($self->{ '_has_order_by' } and uc($token) eq 'BY') { - $self->_add_token( $token ); + $self->_add_token( $token ); $self->{ '_has_order_by' } = 0; next; } - #### - # Set function code delimiter, it can be any string found after - # the AS keyword in function or procedure creation code - #### + #### + # Set function code delimiter, it can be any string found after + # the AS keyword in function or procedure creation code + #### # Toogle _fct_code_delimiter to force next token to be stored as the function code delimiter - if (uc($token) eq 'AS' and !$self->{ '_fct_code_delimiter' } - and $self->{ '_current_sql_stmt' } =~ /^(FUNCTION|PROCEDURE)$/i) { - - if ($self->{ '_is_in_create' }) { + if (uc($token) eq 'AS' and (!$self->{ '_fct_code_delimiter' } || $self->_next_token =~ /CODEPART/) + and $self->{ '_current_sql_stmt' } =~ /^(FUNCTION|PROCEDURE)$/i) + { + if ($self->{ '_is_in_create' }) + { $self->_new_line; - @{ $self->{ '_level_stack' } } = (); - $self->{ '_level' } = 0; - $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + $self->_add_token( $token ); + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); $self->{ '_is_in_create' } = 0; + } else { + $self->_add_token( $token ); } - $self->{ '_fct_code_delimiter' } = '1'; + if ($self->_next_token !~ /CODEPART/) + { + $self->{ '_fct_code_delimiter' } = '1'; + } $self->{ '_is_in_create' } = 0; - $self->_add_token( $token ); next; } + # Store function code delimiter - if ($self->{ '_fct_code_delimiter' } eq '1') { - $self->{ '_fct_code_delimiter' } = $token; + if ($self->{ '_fct_code_delimiter' } eq '1') + { + if ($self->_next_token =~ /CODEPART/) { + $self->{ '_fct_code_delimiter' } = '0'; + } else { + $self->{ '_fct_code_delimiter' } = $token; + } $self->_add_token( $token ); $last = $token; $self->_new_line; $self->_over if (defined $self->_next_token && $self->_next_token !~ /^(DECLARE|BEGIN)$/i); next; } + # Desactivate the block mode when code delimiter is found for the second time - if ($self->{ '_fct_code_delimiter' } && $token eq $self->{ '_fct_code_delimiter' }) { + if ($self->{ '_fct_code_delimiter' } && $token eq $self->{ '_fct_code_delimiter' }) + { $self->{ '_is_in_block' } = -1; @{ $self->{ '_level_stack' } } = (); $self->{ '_level' } = 0; @@ -15413,20 +15623,24 @@ sub beautify { next; } - #### - # Mark when we are parsing a DECLARE or a BLOCK section. When - # entering a BLOCK section store the current indentation level - #### + #### + # Mark when we are parsing a DECLARE or a BLOCK section. When + # entering a BLOCK section store the current indentation level + #### if (uc($token) eq 'DECLARE') { $self->{ '_is_in_block' } = -1; $self->{ '_is_in_declare' } = 1; + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); $self->_new_line; $self->_add_token( $token ); $self->_new_line; $self->_over; - next; - } - elsif ( uc($token) eq 'BEGIN' ) { + next; + } + elsif ( uc($token) eq 'BEGIN' ) + { $self->{ '_is_in_declare' } = 0; if ($self->{ '_is_in_block' } == -1) { @{ $self->{ '_level_stack' } } = (); @@ -15443,41 +15657,47 @@ sub beautify { $self->_over; $self->{ '_is_in_block' }++; } - next; + next; } - #### - # Special case where we want to add a newline into ) AS ( - #### - if (uc($token) eq 'AS' and $last eq ')' and $self->_next_token eq '(') { + #### + # Special case where we want to add a newline into ) AS ( + #### + if (uc($token) eq 'AS' and $last eq ')' and $self->_next_token eq '(') + { $self->_new_line; - # and before RETURNS with increasing indent level - } elsif (uc($token) eq 'RETURNS') { + } + # and before RETURNS with increasing indent level + elsif (uc($token) eq 'RETURNS') + { $self->_new_line; $self->_over; } - if ( $rule ) { + if ( $rule ) + { $self->_process_rule( $rule, $token ); } - elsif ($token =~ /^(LANGUAGE|SECURITY|)$/i) { + elsif ($token =~ /^(LANGUAGE|SECURITY|COST)$/i) + { $self->_new_line; $self->_add_token( $token ); } - elsif ( $token eq '(' ) { + elsif ( $token eq '(' ) + { $self->{ '_is_in_create' }++ if ($self->{ '_is_in_create' }); $self->_add_token( $token, $last ); if ( !$self->{ '_is_in_index' }) { if (uc($last) eq 'AS' || $self->{ '_is_in_create' } == 2 || uc($self->_next_token) eq 'CASE') { $self->_new_line; } - if ($self->{ '_is_in_with' } == 1) { + if ($self->{ '_is_in_with' } == 1) { $self->_over; $self->_new_line; next; - } + } $self->_over; if ($self->{ '_is_in_type' } == 1) { $last = $token; @@ -15487,12 +15707,12 @@ sub beautify { } elsif ( $token eq ')' ) { - if ($self->{ '_is_in_with' } == 1) { + if ($self->{ '_is_in_with' } == 1) { $self->_back; $self->_new_line; $self->_add_token( $token ); next; - } + } if ($self->{ '_is_in_index' }) { $self->_add_token( '' ); $self->_add_token( $token ); @@ -15507,7 +15727,12 @@ sub beautify { ); $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); $self->{ '_is_in_type' }-- if ($self->{ '_is_in_type' }); - $self->_new_line if ($self->{ '_current_sql_stmt' } ne 'INSERT' and !$self->{ '_is_in_function' } and (defined $self->_next_token and $self->_next_token =~ /^(SELECT|WITH)$/i) and $last ne ')'); + $self->_new_line if ($self->{ '_current_sql_stmt' } ne 'INSERT' + and !$self->{ '_is_in_function' } + and (defined $self->_next_token + and $self->_next_token =~ /^(SELECT|WITH)$/i) + and $last ne ')' + ); $self->_back; $self->_add_token( $token ); # Do not go further if this is the last token @@ -15524,7 +15749,7 @@ sub beautify { my $next_tok = quotemeta($self->_next_token); $self->_new_line if (defined $self->_next_token - and $self->_next_token !~ /^AS|THEN|INTO|BETWEEN$/i + and $self->_next_token !~ /^AS|THEN|INTO|BETWEEN|ON$/i and ($self->_next_token !~ /^AND|OR$/i or !$self->{ '_is_in_if' }) and $self->_next_token ne ')' and $self->_next_token !~ /^:/ @@ -15543,12 +15768,13 @@ sub beautify { && !$self->{ '_is_in_function' } && ($self->{ 'comma_break' } || $self->{ '_current_sql_stmt' } ne 'INSERT') && ($self->{ '_current_sql_stmt' } ne 'RAISE') - && ($self->{ '_current_sql_stmt' } !~ /^FUNCTION|PROCEDURE$/ || $self->{ '_fct_code_delimiter' } ne '') + && ($self->{ '_current_sql_stmt' } !~ /^FUNCTION|PROCEDURE$/ + || $self->{ '_fct_code_delimiter' } ne '') && !$self->{ '_is_in_where' } && !$self->{ '_is_in_index' } && $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ && $self->_next_token !~ /^('$|\-\-)/i - && !$self->{ '_parenthesis_function_level' } + && !$self->{ '_parenthesis_function_level' } ); if ($self->{ '_is_in_with' } >= 1 && !$self->{ '_parenthesis_level' }) { @@ -15560,11 +15786,11 @@ sub beautify { } elsif ( $token eq ';' or $token =~ /^\\(?:g|crosstabview|watch)/ ) { # statement separator or executing psql meta command (prefix 'g' includes all its variants) - # Initialize most of statement related variables + # Initialize most of statement related variables $self->{ '_has_from' } = 0; $self->{ '_is_in_where' } = 0; $self->{ '_is_in_from' } = 0; - $self->{ '_is_in_join' } = 0; + $self->{ '_is_in_join' } = 0; $self->{ '_is_in_create' } = 0; $self->{ '_is_in_type' } = 0; $self->{ '_is_in_function' } = 0; @@ -15572,24 +15798,28 @@ sub beautify { $self->{ '_is_in_if' } = 0; $self->{ '_current_sql_stmt' } = ''; $self->{ '_is_in_with' } = 0; - $self->{ '_has_order_by' } = 0; + $self->{ '_has_order_by' } = 0; $self->{ '_has_over_in_join' } = 0; $self->{ '_parenthesis_level' } = 0; - $self->{ '_parenthesis_function_level' } = 0; + $self->{ '_parenthesis_function_level' } = 0; $self->_add_token($token); $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); $self->_new_line; # Add an additional newline after ; when we are not in a function - if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' }) { + if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' }) + { $self->{ '_new_line' } = 0; $self->_new_line; } # End of statement; remove all indentation when we are not in a BEGIN/END block - if (!$self->{ '_is_in_declare' } && $self->{ '_is_in_block' } == -1) { + if (!$self->{ '_is_in_declare' } && $self->{ '_is_in_block' } == -1) + { @{ $self->{ '_level_stack' } } = (); $self->{ '_level' } = 0; $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - } else { + } + else + { if ($#{ $self->{ '_level_stack' } } == -1) { $self->{ '_level' } = ($self->{ '_is_in_declare' }) ? 1 : ($self->{ '_is_in_block' }+1); } else { @@ -15597,76 +15827,101 @@ sub beautify { } } } - elsif ($token =~ /^FOR$/i) { - if ($self->_next_token =~ /^(UPDATE|KEY|NO)$/i) { + elsif ($token =~ /^FOR$/i) + { + if ($self->_next_token =~ /^(UPDATE|KEY|NO)$/i) + { $self->_back; $self->_new_line; } $self->_add_token( $token ); - if ($self->_next_token =~ /^SELECT$/i) { + if ($self->_next_token =~ /^SELECT$/i) + { $self->_new_line; $self->_over; } } - elsif ( $token =~ /^(?:FROM|WHERE|SET|RETURNING|HAVING|VALUES)$/i ) { + elsif ( $token =~ /^(?:FROM|WHERE|SET|RETURNING|HAVING|VALUES)$/i ) + { $self->{ 'no_break' } = 0; - if (uc($last) eq 'DISTINCT' and $token =~ /^FROM$/i) { + if (uc($last) eq 'DISTINCT' and $token =~ /^FROM$/i) + { $self->_add_token( $token ); $last = $token; next; } - if (($token =~ /^FROM$/i) && $self->{ '_has_from' } && !$self->{ '_is_in_function' }) { + if (($token =~ /^FROM$/i) && $self->{ '_has_from' } && !$self->{ '_is_in_function' }) + { $self->{ '_has_from' } = 0; } - if ($token =~ /^FROM$/i) { + if ($token =~ /^FROM$/i) + { $self->{ '_is_in_from' }++ if (!$self->{ '_is_in_function' }); } - if ($token =~ /^WHERE$/i) { - $self->_back() if ($self->{ '_has_over_in_join' }); + if ($token =~ /^WHERE$/i) + { + $self->_back() if ($self->{ '_has_over_in_join' }); $self->{ '_is_in_where' }++; $self->{ '_is_in_from' }-- if ($self->{ '_is_in_from' }); - $self->{ '_is_in_join' } = 0; - $self->{ '_has_over_in_join' } = 0; - } elsif (!$self->{ '_is_in_function' }) { + $self->{ '_is_in_join' } = 0; + $self->{ '_has_over_in_join' } = 0; + } + elsif (!$self->{ '_is_in_function' }) + { $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); } - if ($token =~ /^SET$/i and $self->{ '_is_in_create' }) { + if ($token =~ /^SET$/i and $self->{ '_is_in_create' }) + { # Add newline before SET statement in function header $self->_new_line; - } elsif ($token =~ /^WHERE$/i and $self->{ '_current_sql_stmt' } eq 'DELETE') { + } + elsif ($token =~ /^WHERE$/i and $self->{ '_current_sql_stmt' } eq 'DELETE') + { $self->_new_line; $self->_add_token( $token ); $self->_over; $last = $token; - $self->{ '_is_in_join' } = 0; + $self->{ '_is_in_join' } = 0; next; - } elsif ($token !~ /^FROM$/i or (!$self->{ '_is_in_function' } and $self->{ '_current_sql_stmt' } ne 'DELETE')) { - if ($token !~ /^SET$/i or !$self->{ '_is_in_index' }) { + } + elsif ($token !~ /^FROM$/i or (!$self->{ '_is_in_function' } + and $self->{ '_current_sql_stmt' } ne 'DELETE')) + { + if ($token !~ /^SET$/i or !$self->{ '_is_in_index' }) + { $self->_back; $self->_new_line; } - } else { + } + else + { $self->_add_token( $token ); $last = $token; next; } - if ($token =~ /^VALUES$/i and ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(')) { + if ($token =~ /^VALUES$/i and ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(')) + { $self->_over; } $self->_add_token( $token ); - if ($token =~ /^VALUES$/i and $last eq '(') { + if ($token =~ /^VALUES$/i and $last eq '(') + { $self->_over; } - elsif ( $token =~ /^SET$/i && $self->{ '_current_sql_stmt' } eq 'UPDATE' ) { + elsif ( $token =~ /^SET$/i && $self->{ '_current_sql_stmt' } eq 'UPDATE' ) + { $self->_new_line; $self->_over; } - elsif ( $token !~ /^SET$/i || $self->{ '_current_sql_stmt' } eq 'UPDATE' ) { - if (defined $self->_next_token and $self->_next_token ne '(' and ($self->_next_token !~ /^(UPDATE|KEY|NO)$/i || uc($token) eq 'WHERE')) { + elsif ( $token !~ /^SET$/i || $self->{ '_current_sql_stmt' } eq 'UPDATE' ) + { + if (defined $self->_next_token and $self->_next_token ne '(' + and ($self->_next_token !~ /^(UPDATE|KEY|NO)$/i || uc($token) eq 'WHERE')) + { $self->_new_line; $self->_over; } @@ -15674,28 +15929,37 @@ sub beautify { } # Add newline before INSERT and DELETE if last token was AS (prepared statement) - elsif (defined $last and $token =~ /^INSERT|DELETE$/i and uc($last) eq 'AS') { + elsif (defined $last and $token =~ /^INSERT|DELETE$/i and uc($last) eq 'AS') + { $self->_new_line; $self->_add_token( $token ); } - elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ and $token =~ /^(?:SELECT|PERFORM|UPDATE|DELETE)$/i ) { + elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ + and $token =~ /^(?:SELECT|PERFORM|UPDATE|DELETE)$/i ) + { $self->{ 'no_break' } = 0; - if ($token =~ /^UPDATE$/i and $last =~ /^(FOR|KEY)$/i) { + if ($token =~ /^UPDATE$/i and $last =~ /^(FOR|KEY)$/i) + { $self->_add_token( $token ); - } elsif ($token !~ /^DELETE$/i) { + } + elsif ($token !~ /^DELETE$/i) + { $self->_new_line; $self->_add_token( $token ); $self->_new_line; $self->_over; - } else { + } + else + { $self->_add_token( $token ); } } - elsif ( $token =~ /^(?:GROUP|ORDER|LIMIT|EXCEPTION)$/i ) { - $self->{ '_is_in_join' } = 0; + elsif ( $token =~ /^(?:GROUP|ORDER|LIMIT|EXCEPTION)$/i ) + { + $self->{ '_is_in_join' } = 0; if ($token !~ /^EXCEPTION$/i) { $self->_back; } else { @@ -15704,20 +15968,23 @@ sub beautify { $self->_new_line; $self->_add_token( $token ); # Store current indent position to print END at the right level - if ($token =~ /^EXCEPTION$/i) { + if ($token =~ /^EXCEPTION$/i) + { push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; $self->_over; } $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); } - elsif ( $token =~ /^(?:BY)$/i and $last !~ /^(INCREMENT|OWNED)$/ ) { + elsif ( $token =~ /^(?:BY)$/i and $last !~ /^(INCREMENT|OWNED)$/ ) + { $self->_add_token( $token ); $self->_new_line; $self->_over; } - elsif ( $token =~ /^(?:CASE)$/i ) { + elsif ( $token =~ /^(?:CASE)$/i ) + { $self->_add_token( $token ); # Store current indent position to print END at the right level push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; @@ -15727,7 +15994,8 @@ sub beautify { $self->{ '_is_in_case' }++; } - elsif ( $token =~ /^(?:WHEN)$/i ) { + elsif ( $token =~ /^(?:WHEN)$/i ) + { $self->_back if (!$self->{ '_first_when_in_case' } and defined $last and uc($last) ne 'CASE'); $self->_new_line if (not defined $last or uc($last) ne 'CASE'); $self->_add_token( $token ); @@ -15735,9 +16003,11 @@ sub beautify { $self->{ '_first_when_in_case' } = 0; } - elsif ( $token =~ /^(?:IF|LOOP)$/i ) { + elsif ( $token =~ /^(?:IF|LOOP)$/i ) + { $self->_add_token( $token ); - if (defined $self->_next_token and $self->_next_token ne ';') { + if (defined $self->_next_token and $self->_next_token ne ';') + { $self->_new_line if ($token =~ /^LOOP$/i); $self->_over; push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; @@ -15747,13 +16017,15 @@ sub beautify { } } - elsif ($token =~ /^THEN$/i) { + elsif ($token =~ /^THEN$/i) + { $self->_add_token( $token ); $self->_new_line; $self->{ '_is_in_if' } = 0; } - elsif ( $token =~ /^(?:ELSE|ELSIF)$/i ) { + elsif ( $token =~ /^(?:ELSE|ELSIF)$/i ) + { $self->_back; $self->_new_line; $self->_add_token( $token ); @@ -15761,27 +16033,36 @@ sub beautify { $self->_over; } - elsif ( $token =~ /^(?:END)$/i ) { + elsif ( $token =~ /^(?:END)$/i ) + { $self->{ '_first_when_in_case' } = 0; - if ($self->{ '_is_in_case' }) { - $self->{ '_is_in_case' }--; - $self->_back; - } + if ($self->{ '_is_in_case' }) + { + $self->{ '_is_in_case' }--; + $self->_back; + } # When we are not in a function code block (0 is the main begin/end block of a function) - elsif ($self->{ '_is_in_block' } == -1) { + elsif ($self->{ '_is_in_block' } == -1) + { # END is closing a create function statement so reset position to begining - if ($self->_next_token !~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i) { + if ($self->_next_token !~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i) + { @{ $self->{ '_level_stack' } } = (); $self->{ '_level' } = 0; $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - } else { + } + else + { # otherwise back to last level stored at CASE keyword $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; } # We are in code block - } else { + } + else + { # decrease the block level if this is a END closing a BEGIN block - if ($self->_next_token !~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i) { + if ($self->_next_token !~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i) + { $self->{ '_is_in_block' }--; } # Go back to level stored with IF/LOOP/BEGIN/EXCEPTION block @@ -15792,83 +16073,106 @@ sub beautify { $self->_add_token( $token ); } - elsif ( $token =~ /^(?:UNION|INTERSECT|EXCEPT)$/i ) { + elsif ( $token =~ /^(?:UNION|INTERSECT|EXCEPT)$/i ) + { $self->{ 'no_break' } = 0; - if ($self->{ '_is_in_join' }) { - $self->_back; - $self->{ '_is_in_join' } = 0; - } - $self->_back unless defined $last and $last eq '('; + if ($self->{ '_is_in_join' }) + { + $self->_back; + $self->{ '_is_in_join' } = 0; + } + $self->_back unless defined $last and $last eq '('; $self->_new_line; $self->_add_token( $token ); - $self->_new_line if ( defined $self->_next_token and $self->_next_token ne '(' and $self->_next_token !~ /^ALL$/i ); + $self->_new_line if ( defined $self->_next_token + and $self->_next_token ne '(' + and $self->_next_token !~ /^ALL$/i + ); $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); - $self->{ '_is_in_from' } = 0; + $self->{ '_is_in_from' } = 0; } - elsif ( $token =~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) { + elsif ( $token =~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) + { $self->{ 'no_break' } = 0; - if (!$self->{ '_is_in_join' } and ($last and $last ne ')') ) { - $self->_back; - } - if ($self->{ '_has_over_in_join' }) { - $self->{ '_has_over_in_join' } = 0; - $self->_back; - } + if (!$self->{ '_is_in_join' } and ($last and $last ne ')') ) + { + $self->_back; + } + if ($self->{ '_has_over_in_join' }) + { + $self->{ '_has_over_in_join' } = 0; + $self->_back; + } - if ( $token =~ /(?:LEFT|RIGHT|FULL|CROSS|NATURAL)$/i ) { + if ( $token =~ /(?:LEFT|RIGHT|FULL|CROSS|NATURAL)$/i ) + { $self->_new_line; - $self->_over if ( $self->{ '_level' } == 0 ); + $self->_over if ( $self->{ '_level' } == 0 ); } - if ( ($token =~ /(?:INNER|OUTER)$/i) && ($last !~ /(?:LEFT|RIGHT|CROSS|NATURAL|FULL)$/i) ) { + if ( ($token =~ /(?:INNER|OUTER)$/i) && ($last !~ /(?:LEFT|RIGHT|CROSS|NATURAL|FULL)$/i) ) + { $self->_new_line; $self->_over if (!$self->{ '_is_in_join' }); } $self->_add_token( $token ); } - elsif ( $token =~ /^(?:JOIN)$/i ) { + elsif ( $token =~ /^(?:JOIN)$/i ) + { $self->{ 'no_break' } = 0; - if ( not defined $last or $last !~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) { + if ( not defined $last or $last !~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) + { $self->_new_line; - $self->_back if ($self->{ '_has_over_in_join' }); - $self->{ '_has_over_in_join' } = 0; + $self->_back if ($self->{ '_has_over_in_join' }); + $self->{ '_has_over_in_join' } = 0; } $self->_add_token( $token ); - $self->{ '_is_in_join' } = 1; + $self->{ '_is_in_join' } = 1; } - elsif ( $token =~ /^(?:AND|OR)$/i ) { - # Try to detect AND in BETWEEN clause to prevent newline insert - if (uc($token) eq 'AND' and $self->_next_token() =~ /^\d+$/){ - $self->_add_token( $token ); - next; - } + elsif ( $token =~ /^(?:AND|OR)$/i ) + { + # Try to detect AND in BETWEEN clause to prevent newline insert + if (uc($token) eq 'AND' and $self->_next_token() =~ /^\d+$/) + { + $self->_add_token( $token ); + next; + } $self->{ 'no_break' } = 0; - if ($self->{ '_is_in_join' }) { - $self->_over; - $self->{ '_has_over_in_join' } = 1; - } - $self->{ '_is_in_join' } = 0; - if ( !$self->{ '_is_in_if' } and !$self->{ '_is_in_index' } and (!$last or $last !~ /^(?:CREATE)$/i) ) { + if ($self->{ '_is_in_join' }) + { + $self->_over; + $self->{ '_has_over_in_join' } = 1; + } + $self->{ '_is_in_join' } = 0; + if ( !$self->{ '_is_in_if' } and !$self->{ '_is_in_index' } + and (!$last or $last !~ /^(?:CREATE)$/i) ) + { $self->_new_line; } $self->_add_token( $token ); } - elsif ( $token =~ /^--/ ) { - if ( !$self->{ 'no_comments' } ) { + elsif ( $token =~ /^--/ ) + { + if ( !$self->{ 'no_comments' } ) + { $self->_add_token( $token ); $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); $self->_new_line; $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - } else { + } + else + { $self->_new_line; } } - elsif ( $token =~ /^\/\*.*\*\/$/s ) { - if ( !$self->{ 'no_comments' } ) { + elsif ( $token =~ /^\/\*.*\*\/$/s ) + { + if ( !$self->{ 'no_comments' } ) + { $token =~ s/\n[\s\t]+\*/\n\*/gs; $self->_new_line; $self->_add_token( $token ); @@ -15878,60 +16182,81 @@ sub beautify { } } - elsif ($token =~ /^USING$/i) { - if (!$self->{ '_is_in_from' }) { + elsif ($token =~ /^USING$/i) + { + if (!$self->{ '_is_in_from' }) + { $self->_new_line; - } else { + } + else + { # USING from join clause disable line break $self->{ 'no_break' } = 1; - $self->{ '_is_in_function' }++; + $self->{ '_is_in_function' }++; } $self->_add_token($token); } - elsif ($token =~ /^\\\S/) { # treat everything starting with a \ and at least one character as psql meta command. + elsif ($token =~ /^\\\S/) + { + # treat everything starting with a \ and at least one character as psql meta command. $self->_add_token( $token ); $self->_new_line; } - elsif ($token =~ /^ADD$/i && ($self->{ '_current_sql_stmt' } eq 'SEQUENCE' || $self->{ '_current_sql_stmt' } eq 'ALTER')) { + elsif ($token =~ /^ADD$/i && ($self->{ '_current_sql_stmt' } eq 'SEQUENCE' + || $self->{ '_current_sql_stmt' } eq 'ALTER')) + { $self->_new_line; $self->_over; $self->_add_token($token); } - elsif ($token =~ /^INCREMENT$/i && $self->{ '_current_sql_stmt' } eq 'SEQUENCE') { + elsif ($token =~ /^INCREMENT$/i && $self->{ '_current_sql_stmt' } eq 'SEQUENCE') + { $self->_new_line; $self->_add_token($token); } - elsif ($token =~ /^NO$/i and $self->_next_token =~ /^(MINVALUE|MAXVALUE)$/i) { + elsif ($token =~ /^NO$/i and $self->_next_token =~ /^(MINVALUE|MAXVALUE)$/i) + { $self->_new_line; $self->_add_token($token); } - elsif (uc($last) ne 'NO' and $token =~ /^(MINVALUE|MAXVALUE)$/i) { + elsif (uc($last) ne 'NO' and $token =~ /^(MINVALUE|MAXVALUE)$/i) + { $self->_new_line; $self->_add_token($token); } - elsif ($token =~ /^CACHE$/i) { + elsif ($token =~ /^CACHE$/i) + { $self->_new_line; $self->_add_token($token); } - else { - if ($last =~ /^(?:SEQUENCE)$/i and $self->_next_token !~ /^OWNED$/i) { + else + { + if ($last =~ /^(?:SEQUENCE)$/i and $self->_next_token !~ /^OWNED$/i) + { $self->_add_token( $token ); $self->_new_line; $self->_over; } - else { - if (defined $last && $last eq ')' && (!defined $self->_next_token || $self->_next_token ne ';')) { - if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_from' }) { - $self->{ '_level' } = pop(@{ $self->{ '_level_parenthesis' } }) || 1; - } - } + else + { + if (defined $last && $last eq ')' && (!defined $self->_next_token || $self->_next_token ne ';')) + { + if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_from' }) + { + $self->{ '_level' } = pop(@{ $self->{ '_level_parenthesis' } }) || 1; + } + } $self->_add_token( $token, $last ); + if (defined $last && uc($last) eq 'LANGUAGE' && (!defined $self->_next_token || $self->_next_token ne ';')) + { + $self->_new_line; + } } } @@ -15944,6 +16269,14 @@ sub beautify { return; } +=head2 _add_token + +Add a token to the beautified string. + +Code lifted from SQL::Beautify + +=cut + sub _add_token { my ( $self, $token, $last_token ) = @_; @@ -15975,22 +16308,22 @@ sub _add_token { $self->{ 'content' } .= $sp if (!defined($last_token) && $token); } elsif ( $self->{ '_is_in_create' } == 2 && defined($last_token)) { $self->{ 'content' } .= $sp if ($last_token ne '::' and ($last_token ne '(' || !$self->{ '_is_in_index' })); - } elsif (defined $last_token) { + } elsif (defined $last_token) { $self->{ 'content' } .= $sp if ($last_token eq '(' && $self->{ '_is_in_type' }); } - if ($self->_is_comment($token)) { - my @lines = split(/\n/, $token); - for (my $i = 1; $i <= $#lines; $i++) { - if ($lines[$i] =~ /^\s*\*/) { - $lines[$i] =~ s/^\s*\*/$sp */; - } elsif ($lines[$i] =~ /^\s+[^\*]/) { - $lines[$i] =~ s/^\s+/$sp /; - } - } - $token = join("\n", @lines); - } else { - $token =~ s/\n/\n$sp/gs; - } + if ($self->_is_comment($token)) { + my @lines = split(/\n/, $token); + for (my $i = 1; $i <= $#lines; $i++) { + if ($lines[$i] =~ /^\s*\*/) { + $lines[$i] =~ s/^\s*\*/$sp */; + } elsif ($lines[$i] =~ /^\s+[^\*]/) { + $lines[$i] =~ s/^\s+/$sp /; + } + } + $token = join("\n", @lines); + } else { + $token =~ s/\n/\n$sp/gs; + } } #lowercase/uppercase keywords @@ -16010,7 +16343,7 @@ sub _add_token { # Add formatting for HTML output if ( $self->{ 'colorize' } && $self->{ 'format' } eq 'html' ) { - $token = $self->highlight_code($token, $last_token, $self->_next_token); + $token = $self->highlight_code($token, $last_token, $self->_next_token); } $self->{ 'content' } .= $token; @@ -16020,18 +16353,43 @@ sub _add_token { $self->{ '_new_line' } = 0; } +=head2 _over + +Increase the indentation level. + +Code lifted from SQL::Beautify + +=cut + sub _over { my ( $self ) = @_; ++$self->{ '_level' }; } +=head2 _back + +Decrease the indentation level. + +Code lifted from SQL::Beautify + +=cut + sub _back { my ( $self ) = @_; --$self->{ '_level' } if ( $self->{ '_level' } > 0 ); } +=head2 _indent + +Return a string of spaces according to the current indentation level and the +spaces setting for indenting. + +Code lifted from SQL::Beautify + +=cut + sub _indent { my ( $self ) = @_; @@ -16043,6 +16401,14 @@ sub _indent { } } +=head2 _new_line + +Add a line break, but make sure there are no empty lines. + +Code lifted from SQL::Beautify + +=cut + sub _new_line { my ( $self ) = @_; @@ -16050,24 +16416,55 @@ sub _new_line { $self->{ '_new_line' } = 1; } +=head2 _next_token + +Have a look at the token that's coming up next. + +Code lifted from SQL::Beautify + +=cut + sub _next_token { my ( $self ) = @_; return @{ $self->{ '_tokens' } } ? $self->{ '_tokens' }->[ 0 ] : undef; } +=head2 _token + +Get the next token, removing it from the list of remaining tokens. + +Code lifted from SQL::Beautify + +=cut + sub _token { my ( $self ) = @_; return shift @{ $self->{ '_tokens' } }; } +=head2 _is_keyword + +Check if a token is a known SQL keyword. + +Code lifted from SQL::Beautify + +=cut + sub _is_keyword { my ( $self, $token ) = @_; return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'keywords' } }; } +=head2 _is_comment + +Check if a token is a SQL or C style comment + +=cut + + sub _is_comment { my ( $self, $token ) = @_; @@ -16076,6 +16473,14 @@ sub _is_comment { return 0; } +=head2 _is_function + +Check if a token is a known SQL function. + +Code lifted from SQL::Beautify + +=cut + sub _is_function { my ( $self, $token ) = @_; @@ -16084,6 +16489,14 @@ sub _is_function { return $ret[ 0 ]; } +=head2 add_keywords + +Add new keywords to highlight. + +Code lifted from SQL::Beautify + +=cut + sub add_keywords { my $self = shift; @@ -16092,6 +16505,14 @@ sub add_keywords { } } +=head2 add_functions + +Add new functions to highlight. + +Code lifted from SQL::Beautify + +=cut + sub add_functions { my $self = shift; @@ -16100,6 +16521,14 @@ sub add_functions { } } +=head2 add_rule + +Add new rules. + +Code lifted from SQL::Beautify + +=cut + sub add_rule { my ( $self, $format, $token ) = @_; @@ -16109,6 +16538,14 @@ sub add_rule { push @{ $group }, ref( $token ) ? @{ $token } : $token; } +=head2 _get_rule + +Find custom rule for a token. + +Code lifted from SQL::Beautify + +=cut + sub _get_rule { my ( $self, $token ) = @_; @@ -16121,6 +16558,14 @@ sub _get_rule { return; } +=head2 _process_rule + +Applies defined rule. + +Code lifted from SQL::Beautify + +=cut + sub _process_rule { my ( $self, $rule, $token ) = @_; @@ -16139,20 +16584,45 @@ sub _process_rule { } } +=head2 _is_constant + +Check if a token is a constant. + +Code lifted from SQL::Beautify + +=cut + sub _is_constant { my ( $self, $token ) = @_; return ( $token =~ /^\d+$/ or $token =~ /^(['"`]).*\1$/ ); } +=head2 _is_punctuation + +Check if a token is punctuation. + +Code lifted from SQL::Beautify + +=cut + sub _is_punctuation { my ( $self, $token ) = @_; if ($self->{ 'comma' } eq 'start' and $token eq ',') { - return 0; + return 0; } return ( $token =~ /^[,;.]$/ ); } +=head2 _generate_anonymized_string + +Simply generate a random string, thanks to Perlmonks. + +Returns original in certain cases which don't require anonymization, like +timestamps, or intervals. + +=cut + sub _generate_anonymized_string { my $self = shift; my ( $before, $original, $after ) = @_; @@ -16212,6 +16682,12 @@ sub _generate_anonymized_string { return $cache->{ $original }; } +=head2 anonymize + +Anonymize litteral in SQL queries by replacing parameters with fake values + +=cut + sub anonymize { my $self = shift; my $query = $self->query; @@ -16241,6 +16717,40 @@ sub anonymize { $self->query( $query ); } +=head2 set_defaults + +Sets defaults for newly created objects. + +Currently defined defaults: + +=over + +=item spaces => 4 + +=item space => ' ' + +=item break => "\n" + +=item uc_keywords => 0 + +=item uc_functions => 0 + +=item no_comments => 0 + +=item placeholder => '' + +=item separator => '' + +=item comma => 'end' + +=item format => 'text' + +=item colorize => 1 + +=back + +=cut + sub set_defaults { my $self = shift; $self->set_dicts(); @@ -16266,17 +16776,34 @@ sub set_defaults { return; } +=head2 format + +Set output format - possible values: 'text' and 'html' + +Default is text output. Returns 0 in case or wrong format and use default. + +=cut + sub format { my $self = shift; my $format = shift; if ( grep(/^$format$/i, 'text', 'html') ) { $self->{ 'format' } = lc($format); - return 1; + return 1; } return 0; } +=head2 set_dicts + +Sets various dictionaries (lists of keywords, functions, symbols, and the like) + +This was moved to separate function, so it can be put at the very end of module +so it will be easier to read the rest of the code. + +=cut + sub set_dicts { my $self = shift; @@ -16666,6 +17193,13 @@ sub set_dicts { return; } +=head2 _remove_dynamic_code + +Internal function used to hide dynamic code in plpgsql to the parser. +The original values are restored with function _restore_dynamic_code(). + +=cut + sub _remove_dynamic_code { my ($self, $str, $code_sep) = @_; @@ -16676,9 +17210,9 @@ sub _remove_dynamic_code # Try to auto detect the string separator if none are provided. # Note that default single quote separtor is natively supported. if ($#dynsep == -1) { - # if a dollar sign is found after EXECUTE then the following string - # until an other dollar is found will be understand as a text delimiter - @dynsep = $$str =~ /EXECUTE\s+(\$[^\$\s]*\$)/igs; + # if a dollar sign is found after EXECUTE then the following string + # until an other dollar is found will be understand as a text delimiter + @dynsep = $$str =~ /EXECUTE\s+(\$[^\$\s]*\$)/igs; } my $idx = 0; @@ -16696,6 +17230,13 @@ sub _remove_dynamic_code } } +=head2 _restore_dynamic_code + +Internal function used to restore plpgsql dynamic code in plpgsql +that was removed by the _remove_dynamic_code() method. + +=cut + sub _restore_dynamic_code { my ($self, $str) = @_; @@ -16704,6 +17245,14 @@ sub _restore_dynamic_code } +=head2 _remove_comments (OBSOLETE) + +Internal function used to remove comments in SQL code +to simplify the work of the parser. Comments are restored +with the _restore_comments() method. + +=cut + sub _remove_comments { my ($self, $content) = @_; @@ -16717,7 +17266,7 @@ sub _remove_comments my @lines = split(/\n/, $$content); for (my $j = 0; $j <= $#lines; $j++) { - $lines[$j] //= ''; + $lines[$j] //= ''; # Extract multiline comments as a single placeholder my $old_j = $j; my $cmt = ''; @@ -16762,6 +17311,13 @@ sub _remove_comments } } +=head2 _restore_comments (OBSOLETE) + +Internal function used to restore comments in SQL code +that was removed by the _remove_comments() method. + +=cut + sub _restore_comments { my ($self, $content) = @_; @@ -16769,6 +17325,30 @@ sub _restore_comments while ($$content =~ s/(PGF_COMMENT\d+A)[\n]*/$self->{'comments'}{$1}\n/s) { delete $self->{'comments'}{$1}; }; } + + +=head1 AUTHOR + +pgFormatter is an original work from Gilles Darold + +=head1 BUGS + +Please report any bugs or feature requests to: https://github.com/darold/pgFormatter/issues + +=head1 COPYRIGHT + +Copyright 2012-2018 Gilles Darold. All rights reserved. + +=head1 LICENSE + +pgFormatter is free software distributed under the PostgreSQL Licence. + +A modified version of the SQL::Beautify Perl Module is embedded in pgFormatter +with copyright (C) 2009 by Jonas Kramer and is published under the terms of +the Artistic License 2.0. + +=cut + 1; }