# Inclusion of Perl package pgFormatter::Beautify.
{
-
package pgFormatter::Beautify;
use strict;
# 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';
# 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<set_defaults>.
+
+=cut
+
sub new {
my $class = shift;
my %options = @_;
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++;
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;
#$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) = @_;
while ( my ( $k, $v ) = each %{ $self->{ 'dict' }->{ 'symbols' } } ) {
if ($token eq $k) {
$token = '<span class="sy0">' . $v . '</span>';
- return $token;
- }
+ return $token;
+ }
}
# lowercase/uppercase keywords
if ( $self->_is_keyword( $token ) ) {
- if ( $self->{ 'uc_keywords' } == 1 ) {
- $token = '<span class="kw1_l">' . $token . '</span>';
- } elsif ( $self->{ 'uc_keywords' } == 2 ) {
- $token = '<span class="kw1_u">' . $token . '</span>';
- } elsif ( $self->{ 'uc_keywords' } == 3 ) {
- $token = '<span class="kw1_c">' . $token . '</span>';
- } else {
- $token = '<span class="kw1">' . $token . '</span>';
- }
- return $token;
+ if ( $self->{ 'uc_keywords' } == 1 ) {
+ $token = '<span class="kw1_l">' . $token . '</span>';
+ } elsif ( $self->{ 'uc_keywords' } == 2 ) {
+ $token = '<span class="kw1_u">' . $token . '</span>';
+ } elsif ( $self->{ 'uc_keywords' } == 3 ) {
+ $token = '<span class="kw1_c">' . $token . '</span>';
+ } else {
+ $token = '<span class="kw1">' . $token . '</span>';
+ }
+ 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 = '<span class="kw2_l">' . $token . '</span>';
- } elsif ($self->{ 'uc_functions' } == 2) {
- $token = '<span class="kw2_u">' . $token . '</span>';
- } elsif ($self->{ 'uc_functions' } == 3) {
- $token = '<span class="kw2_c">' . $token . '</span>';
- } else {
- $token = '<span class="kw2">' . $token . '</span>';
- }
- return $token;
+ if ($self->{ 'uc_functions' } == 1) {
+ $token = '<span class="kw2_l">' . $token . '</span>';
+ } elsif ($self->{ 'uc_functions' } == 2) {
+ $token = '<span class="kw2_u">' . $token . '</span>';
+ } elsif ($self->{ 'uc_functions' } == 3) {
+ $token = '<span class="kw2_c">' . $token . '</span>';
+ } else {
+ $token = '<span class="kw2">' . $token . '</span>';
+ }
+ return $token;
}
# Colorize STDIN/STDOUT in COPY statement
if ( grep(/^\Q$token\E$/i, @{ $self->{ 'dict' }->{ 'copy_keywords' } }) ) {
- if ($self->{ 'uc_keywords' } == 1) {
- $token = '<span class="kw3_!">' . $token . '</span>';
- } elsif ($self->{ 'uc_keywords' } == 2) {
- $token = '<span class="kw3_u">' . $token . '</span>';
- } elsif ($self->{ 'uc_keywords' } == 3) {
- $token = '<span class="kw3_c">' . $token . '</span>';
- } else {
- $token = '<span class="kw3">' . $token . '</span>';
- }
- return $token;
+ if ($self->{ 'uc_keywords' } == 1) {
+ $token = '<span class="kw3_!">' . $token . '</span>';
+ } elsif ($self->{ 'uc_keywords' } == 2) {
+ $token = '<span class="kw3_u">' . $token . '</span>';
+ } elsif ($self->{ 'uc_keywords' } == 3) {
+ $token = '<span class="kw3_c">' . $token . '</span>';
+ } else {
+ $token = '<span class="kw3">' . $token . '</span>';
+ }
+ return $token;
}
# Colorize parenthesis
if ( grep(/^\Q$token\E$/i, @{ $self->{ 'dict' }->{ 'brackets' } }) ) {
$token = '<span class="br0">' . $token . '</span>';
- return $token;
+ return $token;
}
# Colorize comment
if ( $self->_is_comment( $token ) ) {
$token = '<span class="br1">' . $token . '</span>';
- return $token;
+ return $token;
}
# Colorize numbers
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();
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;
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' }) {
}
}
- ####
- # 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' }) {
# 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;
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' } } = ();
$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;
}
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 );
);
$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
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 !~ /^:/
&& !$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' }) {
}
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;
$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 {
}
}
}
- 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;
}
}
# 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 {
$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' };
$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 );
$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' };
}
}
- 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 );
$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
$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 );
}
}
- 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;
+ }
}
}
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 ) = @_;
$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
# 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;
$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 ) = @_;
}
}
+=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 ) = @_;
$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 ) = @_;
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 ) = @_;
return $ret[ 0 ];
}
+=head2 add_keywords
+
+Add new keywords to highlight.
+
+Code lifted from SQL::Beautify
+
+=cut
+
sub add_keywords {
my $self = shift;
}
}
+=head2 add_functions
+
+Add new functions to highlight.
+
+Code lifted from SQL::Beautify
+
+=cut
+
sub add_functions {
my $self = shift;
}
}
+=head2 add_rule
+
+Add new rules.
+
+Code lifted from SQL::Beautify
+
+=cut
+
sub add_rule {
my ( $self, $format, $token ) = @_;
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 ) = @_;
return;
}
+=head2 _process_rule
+
+Applies defined rule.
+
+Code lifted from SQL::Beautify
+
+=cut
+
sub _process_rule {
my ( $self, $rule, $token ) = @_;
}
}
+=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 ) = @_;
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;
$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();
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;
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) = @_;
# 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;
}
}
+=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) = @_;
}
+=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) = @_;
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 = '';
}
}
+=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) = @_;
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;
}