From: Gilles Darold Date: Fri, 21 Jun 2019 20:35:08 +0000 (+0200) Subject: Update code formatter to pgFormatter 4.0. X-Git-Tag: v11.0~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0f53d2f23ca65e07a0b1c5a797358a73299b5f6a;p=pgbadger Update code formatter to pgFormatter 4.0. --- diff --git a/pgbadger b/pgbadger index 2b71309..cdfd277 100755 --- a/pgbadger +++ b/pgbadger @@ -377,7 +377,7 @@ my @session_closed_msg = ( qr/^no pg_hba.conf entry for/, ); -my $sql_prettified; +my $sql_prettified = pgFormatter::Beautify->new('colorize' => 1, 'format' => 'html'); # Do not display data in pie where percentage is lower than this value # to avoid label overlapping. @@ -1753,11 +1753,6 @@ if ( !$incremental && ($#given_log_files >= 0) ) } else { - # Create instance to prettify SQL query - if (!$noprettify) - { - $sql_prettified = pgFormatter::Beautify->new(); - } &dump_as_html('.', $db); } $fh->close; @@ -2467,9 +2462,6 @@ sub build_incremental_reports localdie("FATAL: can't write to $tmp_dir/$bpath/$current_out_file, $!\n"); } # Create instance to prettify SQL query - if (!$noprettify) { - $sql_prettified = pgFormatter::Beautify->new(); - } &dump_as_html('../../..', $db); $fh->close; } @@ -2522,9 +2514,6 @@ sub build_incremental_reports localdie("FATAL: can't write to $tmp_dir/$wdir/$current_out_file, $!\n"); } # Create instance to prettify SQL query - if (!$noprettify) { - $sql_prettified = pgFormatter::Beautify->new(); - } &dump_as_html('../..', $db); $fh->close; } @@ -13272,55 +13261,8 @@ sub highlight_code $sql_prettified->beautify(); $code = $sql_prettified->content(); } - return $code if ($nohighlight); - my $i = 0; - my @qqcode = (); - while ($code =~ s/("[^\"]*")/QQCODEY${i}A/s) { - push(@qqcode, $1); - $i++; - } - $i = 0; - my @qcode = (); - while ($code =~ s/('[^\']*')/QCODEY${i}B/s) { - push(@qcode, $1); - $i++; - } - - foreach my $x (sort keys %{ $sql_prettified->{ 'dict' }->{ 'symbols' } }) { - $code =~ s/\Q$x\E/\$\$PGBGYA\$\$$sql_prettified->{ 'dict' }->{ 'symbols' }{$x}\$\$PGBGYB\$\$/gs; - } - for (my $x = 0 ; $x <= $#{ $sql_prettified->{ 'dict' }->{ 'pg_keywords' } } ; $x++) { - $code =~ s/(?{ 'dict' }->{ 'pg_keywords' }[$x]\b/$sql_prettified->{ 'dict' }->{ 'pg_keywords' }[$x]<\/span>/igs; - } - - for (my $x = 0 ; $x <= $#{ $sql_prettified->{ 'dict' }->{ 'pg_functions' } } ; $x++) { - $code =~ s/(?{ 'dict' }->{ 'pg_functions' }[$x]\b/$sql_prettified->{ 'dict' }->{ 'pg_functions' }[$x]<\/span>/igs; - } - for (my $x = 0 ; $x <= $#{ $sql_prettified->{ 'dict' }->{ 'copy_keywords' } } ; $x++) { - $code =~ s/\b$sql_prettified->{ 'dict' }->{ 'copy_keywords' }[$x]\b/$sql_prettified->{ 'dict' }->{ 'copy_keywords' }[$x]<\/span>/igs; - } - for (my $x = 0 ; $x <= $#{ $sql_prettified->{ 'dict' }->{ 'brackets' } } ; $x++) { - $code =~ s/(\Q$sql_prettified->{ 'dict' }->{ 'brackets' }[$x]\E)/$1<\/span>/igs; - } - - $code =~ s/\$\$PGBGYA\$\$([^\$]+)\$\$PGBGYB\$\$/$1<\/span>/gs; - - $code =~ s/\b(\d+)\b/$1<\/span>/igs; - - for (my $x = 0; $x <= $#qcode; $x++) { - $code =~ s/QCODEY${x}B/$qcode[$x]/s; - } - for (my $x = 0; $x <= $#qqcode; $x++) { - $code =~ s/QQCODEY${x}A/$qqcode[$x]/s; - } - - $code =~ s/('[^']*')/$1<\/span>/gs; - $code =~ s/(`[^`]*`)/$1<\/span>/gs; - - $code =~ s/\$\$PGBGY(A|B)\$\$//gs; - return encode('UTF-8', $code); } @@ -16318,6 +16260,10 @@ use warnings; use warnings qw( FATAL ); use Encode qw( decode ); +use Text::Wrap; + +our $DEBUG = 0; + # PostgreSQL functions that use a FROM clause our @have_from_clause = qw( extract overlay substring trim ); @@ -16327,12 +16273,12 @@ pgFormatter::Beautify - Library for pretty-printing SQL queries =head1 VERSION -Version 3.0 +Version 4.0 =cut # Version of pgFormatter -our $VERSION = '3.0'; +our $VERSION = '4.1dev'; # Inclusion of code from Perl package SQL::Beautify # Copyright (C) 2009 by Jonas Kramer @@ -16361,6 +16307,11 @@ Example usage: $beautifier->beautify(); my $nice_anonymized_html = $beautifier->content(); + $beautifier->format(); + $beautifier->beautify(); + $beautifier->wrap_lines() + my $wrapped_txt = $beautifier->content(); + =head1 FUNCTIONS =head2 new @@ -16433,6 +16384,12 @@ Takes options as hash. Following options are recognized: =item * wrap - wraps given keywords in pre- and post- markup. Specific docs in SQL::Beautify +=item * format_type - try an other formatting + +=item * wrap_limit - wrap queries at a certain length + +=item * wrap_after - number of column after which lists must be wrapped + =back For defaults, please check function L. @@ -16446,10 +16403,12 @@ sub new { my $self = bless {}, $class; $self->set_defaults(); - for my $key ( qw( query spaces space break wrap keywords functions rules uc_keywords uc_functions no_comments placeholder separator comma comma_break format colorize) ) { - $self->{ $key } = $options{ $key } if defined $options{ $key }; + for my $key ( qw( query spaces space break wrap keywords functions rules uc_keywords uc_functions no_comments placeholder separator comma comma_break format colorize format_type wrap_limit wrap_after) ) { + $self->{ $key } = $options{ $key } if defined $options{ $key }; } + $self->_refresh_functions_re(); + # Make sure "break" is sensible $self->{ 'break' } = ' ' if $self->{ 'spaces' } == 0; @@ -16464,14 +16423,18 @@ sub new { # Check comma value, when invalid set to default: end if (lc($self->{ 'comma' }) ne 'start') { - $self->{ 'comma' } = 'end'; + $self->{ 'comma' } = 'end'; } else { - $self->{ 'comma' } = lc($self->{ 'comma' }); + $self->{ 'comma' } = lc($self->{ 'comma' }); } $self->{ 'format' } //= 'text'; $self->{ 'colorize' } //= 1; + $self->{ 'format_type' } //= 0; + $self->{ 'wrap_limit' } //= 0; + $self->{ 'wrap_after' } //= 0; + return $self; } @@ -16497,50 +16460,60 @@ sub query { 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); + for (my $j = 0; $j <= $#temp_content; $j++) { + next if ($temp_content[$j] =~ /^CREATE/i); + # Remove any call too CREATE/DROP LANGUAGE to not break search of function code separator + $temp_content[$j] =~ s/(CREATE|DROP)\s+LANGUAGE\s+[^;]+;.*//is; + # Fix case where code separator with $ is associated to begin/end keywords + $temp_content[$j] =~ s/([^\s]+\$)(BEGIN\s)/$1 $2/igs; + $temp_content[$j] =~ s/(\sEND)(\$[^\s]+)/$1 $2/igs; + $temp_content[$j] =~ s/(CREATE|DROP)\s+LANGUAGE\s+[^;]+;.*//is; + 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); + 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; - } - } - } - } + next if (!$code_sep); + 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' }) { - while ( $self->{ 'query' } =~ s/($self->{ 'placeholder' })/PLACEHOLDER${i}PLACEHOLDER/) { - push(@{ $self->{ 'placeholder_values' } }, $1); - $i++; + while ( $self->{ 'query' } =~ s/($self->{ 'placeholder' })/PLACEHOLDER${i}PLACEHOLDER/) { + push(@{ $self->{ 'placeholder_values' } }, $1); + $i++; } } # Replace dynamic code with placeholder $self->_remove_dynamic_code( \$self->{ 'query' }, $self->{ 'separator' } ); + # Replace operator with placeholder + $self->_quote_operator( \$self->{ 'query' } ); + return $self->{ 'query' }; } @@ -16553,26 +16526,25 @@ client with the $object->format() method. =cut -sub content { +sub content +{ my $self = shift; my $new_value = shift; $self->{ 'content' } = $new_value if defined $new_value; - - # Hide comment beside a placeholder for easy parsing: OBSOLETE - #$self->_remove_comments( \$self->{ 'content' } ); + $self->{ 'content' } =~ s/\(\s+\(/\(\(/gs; # Replace placeholders with their original dynamic code $self->_restore_dynamic_code( \$self->{ 'content' } ); - # Restore comments inplace: OBSOLETE - #$self->_restore_comments(\$self->{ 'content' }); + # Replace placeholders with their original operator + $self->_restore_operator( \$self->{ 'content' } ); # Replace placeholders by their original values 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; + $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' }; @@ -16584,7 +16556,8 @@ Makes result html with styles set for highlighting. =cut -sub highlight_code { +sub highlight_code +{ my ($self, $token, $last_token, $next_token) = @_; # Do not use uninitialized variable @@ -16593,64 +16566,67 @@ sub highlight_code { # Colorize operators while ( my ( $k, $v ) = each %{ $self->{ 'dict' }->{ 'symbols' } } ) { - if ($token eq $k) { - $token = '' . $v . ''; - return $token; - } + if ($token eq $k) { + $token = '' . $v . ''; + 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; + # lowercase/uppercase keywords taking care of function with same name + if ( $self->_is_keyword( $token ) && (!$self->_is_function( $token ) || $next_token ne '(') ) { + 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; + # lowercase/uppercase known functions or words followed by an open parenthesis + # if the token is not a keyword, an open parenthesis or a comment + if (($self->_is_function( $token ) && $next_token eq '(') + || (!$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; } # 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; + $token = '' . $token . ''; + return $token; } # Colorize comment if ( $self->_is_comment( $token ) ) { - $token = '' . $token . ''; - return $token; + $token = '' . $token . ''; + return $token; } # Colorize numbers @@ -16676,64 +16652,64 @@ sub tokenize_sql { my $query = $self->query(); my $re = qr{ - ( - (?:\\(?:copyright|errverbose|g|gx|gexec|gset|q|crosstabview|watch|\?|h|e|ef|ev|p|r|s|w|copy|echo|i|ir|o|qecho|if|elif|else|endif|d(?:[aAbcCdDfFgilLmnoOpstTuvExy]|dp|et|es|eu|ew|fa|fn|ft|fw|Fd|Fp|Ft|rds|Rp|Rs)?S?\+?|l\+?|sf\+?|sv\+?|z|a|C|f|H|pset|t|T|x|c|connect|encoding|password|conninfo|cd|setenv|timing|\!|prompt|set|unset|lo_export|lo_import|lo_list|lo_unlink))(?:$|[\n]|[\ \t](?:(?!\\\\)[\ \t\S])*) # psql meta-command - | - (?:--)[\ \t\S]* # single line comments - | - (?:\-\|\-) # range operator "is adjacent to" - | - (?:\->>|\->|\#>>|\#>|\?\&|\?) # Json Operators - | - (?:\#<=|\#>=|\#<>|\#<|\#=) # compares tinterval and reltime - | - (?:>>=|<<=) # inet operators - | - (?:!!|\@\@\@) # deprecated factorial and full text search operators - | - (?:\|\|\/|\|\/) # square root and cube root - | - (?:\@\-\@|\@\@|\#\#|<\->|<<\||\|>>|\&<\||\&<|\|\&>|\&>|<\^|>\^|\?\#|\#|\?<\||\?\-\||\?\-|\?\|\||\?\||\@>|<\@|\~=) - # Geometric Operators - | - (?:~<=~|~>=~|~>~|~<~) # string comparison for pattern matching operator families - | - (?:!~~|!~~\*|~~\*|~~) # LIKE operators - | - (?:!~\*|!~|~\*) # regular expression operators - | - (?:\*=|\*<>|\*<=|\*>=|\*<|\*>) # composite type comparison operators - | - (?:<>|<=>|>=|<=|==|!=|:=|=|!|<<|>>|<|>|\|\||\||&&|&|-|\+|\*(?!/)|/(?!\*)|\%|~|\^|\?) # operators and tests - | - [\[\]\(\),;.] # punctuation (parenthesis, comma) - | - E\'\'(?!\') # empty single escaped quoted string - | - \'\'(?!\') # empty single quoted string - | - \"\"(?!\"") # empty double quoted string - | - "(?>(?:(?>[^"\\]+)|""|\\.)*)+" # anything inside double quotes, ungreedy - | - `(?>(?:(?>[^`\\]+)|``|\\.)*)+` # anything inside backticks quotes, ungreedy - | - E'(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything escaped inside single quotes, ungreedy. - | - '(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything inside single quotes, ungreedy. - | - /\*[\ \t\r\n\S]*?\*/ # C style comments - | - (?:[\w:@]+(?:\.(?:\w+|\*)?)*) # words, standard named placeholders, db.table.*, db.* - | - (?:\$\w+\$) - | - (?: \$_\$ | \$\d+ | \${1,2} | \$\w+\$ ) # dollar expressions - eg $_$ $3 $$ $BODY$ - | - \n # newline - | - [\t\ ]+ # any kind of white spaces - ) + ( + (?:\\(?:copyright|errverbose|g|gx|gexec|gset|q|crosstabview|watch|\?|h|e|ef|ev|p|r|s|w|copy|echo|i|ir|o|qecho|if|elif|else|endif|d(?:[aAbcCdDfFgilLmnoOpstTuvExy]|dp|et|es|eu|ew|fa|fn|ft|fw|Fd|Fp|Ft|rds|Rp|Rs)?S?\+?|l\+?|sf\+?|sv\+?|z|a|C|f|H|pset|t|T|x|c|connect|encoding|password|conninfo|cd|setenv|timing|\!|prompt|set|unset|lo_export|lo_import|lo_list|lo_unlink))(?:$|[\n]|[\ \t](?:(?!\\\\)[\ \t\S])*) # psql meta-command + | + (?:\s*--)[\ \t\S]* # single line comments + | + (?:\-\|\-) # range operator "is adjacent to" + | + (?:\->>|\->|\#>>|\#>|\?\&|\?) # Json Operators + | + (?:\#<=|\#>=|\#<>|\#<|\#=) # compares tinterval and reltime + | + (?:>>=|<<=) # inet operators + | + (?:!!|\@\@\@) # deprecated factorial and full text search operators + | + (?:\|\|\/|\|\/) # square root and cube root + | + (?:\@\-\@|\@\@|\#\#|<\->|<<\||\|>>|\&<\||\&<|\|\&>|\&>|<\^|>\^|\?\#|\#|\?<\||\?\-\||\?\-|\?\|\||\?\||\@>|<\@|\~=) + # Geometric Operators + | + (?:~<=~|~>=~|~>~|~<~) # string comparison for pattern matching operator families + | + (?:!~~|!~~\*|~~\*|~~) # LIKE operators + | + (?:!~\*|!~|~\*) # regular expression operators + | + (?:\*=|\*<>|\*<=|\*>=|\*<|\*>) # composite type comparison operators + | + (?:<>|<=>|>=|<=|=>|==|!=|:=|=|!|<<|>>|<|>|\|\||\||&&|&|-|\+|\*(?!/)|/(?!\*)|\%|~|\^|\?) # operators and tests + | + [\[\]\(\),;.] # punctuation (parenthesis, comma) + | + E\'\'(?!\') # empty single escaped quoted string + | + \'\'(?!\') # empty single quoted string + | + \"\"(?!\"") # empty double quoted string + | + "(?>(?:(?>[^"\\]+)|""|\\.)*)+" # anything inside double quotes, ungreedy + | + `(?>(?:(?>[^`\\]+)|``|\\.)*)+` # anything inside backticks quotes, ungreedy + | + E'(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything escaped inside single quotes, ungreedy. + | + '(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything inside single quotes, ungreedy. + | + /\*[\ \t\r\n\S]*?\*/ # C style comments + | + (?:[\w:\@]+[\$]*[\w:\@]*(?:\.(?:\w+|\*)?)*) # words, standard named placeholders, db.table.*, db.* + | + (?:\$\w+\$) + | + (?: \$_\$ | \$\d+ | \${1,2} | \$\w+\$ ) # dollar expressions - eg $_$ $3 $$ $BODY$ + | + \n # newline + | + [\t\ ]+ # any kind of white spaces + ) }smx; my @query = (); @@ -16761,6 +16737,7 @@ sub beautify { # Main variables used to store differents state $self->content( '' ); + $self->{ '_level' } = 0; $self->{ '_level_stack' } = []; $self->{ '_level_parenthesis' } = []; $self->{ '_new_line' } = 1; @@ -16769,859 +16746,1545 @@ sub beautify { $self->{ '_fct_code_delimiter' } = ''; $self->{ '_first_when_in_case' } = 0; - $self->{ '_has_from' } = 0; + $self->{ '_is_in_conversion' } = 0; $self->{ '_is_in_case' } = 0; $self->{ '_is_in_where' } = 0; $self->{ '_is_in_from' } = 0; $self->{ '_is_in_join' } = 0; $self->{ '_is_in_create' } = 0; + $self->{ '_is_in_rule' } = 0; + $self->{ '_is_in_create_function' } = 0; + $self->{ '_is_in_alter' } = 0; + $self->{ '_is_in_trigger' } = 0; + $self->{ '_is_in_publication' } = 0; + $self->{ '_is_in_call' } = 0; $self->{ '_is_in_type' } = 0; $self->{ '_is_in_declare' } = 0; $self->{ '_is_in_block' } = -1; + $self->{ '_is_in_work' } = 0; $self->{ '_is_in_function' } = 0; + $self->{ '_is_in_procedure' } = 0; $self->{ '_is_in_index' } = 0; $self->{ '_is_in_with' } = 0; + $self->{ '_is_in_explain' } = 0; $self->{ '_parenthesis_level' } = 0; $self->{ '_parenthesis_function_level' } = 0; $self->{ '_has_order_by' } = 0; $self->{ '_has_over_in_join' } = 0; + $self->{ '_insert_values' } = 0; + $self->{ '_is_in_constraint' } = 0; + $self->{ '_is_in_distinct' } = 0; + $self->{ '_is_in_array' } = 0; + $self->{ '_is_in_filter' } = 0; + $self->{ '_parenthesis_filter_level' } = 0; + $self->{ '_is_in_within' } = 0; + $self->{ '_is_in_grouping' } = 0; + $self->{ '_is_in_partition' } = 0; + $self->{ '_is_in_over' } = 0; + $self->{ '_is_in_policy' } = 0; + $self->{ '_is_in_using' } = 0; + $self->{ '_and_level' } = 0; + $self->{ '_col_count' } = 0; + $self->{ '_is_in_drop' } = 0; + $self->{ '_is_in_operator' } = 0; + $self->{ '_is_in_exception' } = 0; + $self->{ '_is_in_sub_query' } = 0; + $self->{ '_is_in_fetch' } = 0; + $self->{ '_is_in_aggregate' } = 0; + $self->{ '_is_in_value' } = 0; + $self->{ '_parenthesis_level_value' } = 0; my $last = ''; my @token_array = $self->tokenize_sql(); - while ( defined( my $token = $self->_token ) ) { - my $rule = $self->_get_rule( $token ); - - #### - # 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' }++; + while ( defined( my $token = $self->_token ) ) + { + my $rule = $self->_get_rule( $token ); + + # Replace concat operator found in some SGBD into || for normalization + if (lc($token) eq 'concat' && defined $self->_next_token() && $self->_next_token ne '(') { + $token = '||'; + } + + #### + # 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 = lc($token); + $word =~ s/^[^\.]+\.//; + $word =~ s/^:://; + if (uc($last) eq 'FUNCTION' and $token =~ /^\d+$/) { + $self->{ '_is_in_function' }++; + } elsif ($word && exists $self->{ 'dict' }->{ 'pg_functions' }{$word}) { + $self->{ '_is_in_function' }++; + # Try to detect user defined functions + } elsif ($last ne '*' and !$self->_is_keyword($token) + and (exists $self->{ 'dict' }->{ 'symbols' }{ $last } + or $last =~ /^\d+$/) + ) + { + $self->{ '_is_in_function' }++; } - } + } + + #### + # Set open parenthesis position to know if we + # are in subqueries or function parameters + #### + if ( $token eq ')') + { + $self->{ '_parenthesis_filter_level' }-- if ($self->{ '_is_in_filter' } and $self->{ '_parenthesis_filter_level' }); + $self->{ '_is_in_filter' } = 0 if (!$self->{ '_parenthesis_filter_level' }); + + if (!$self->{ '_is_in_function' }) { + $self->{ '_parenthesis_level' }-- if ($self->{ '_parenthesis_level' } > 0); + } else { + $self->{ '_parenthesis_function_level' }-- if ($self->{ '_parenthesis_function_level' } > 0); + if (!$self->{ '_parenthesis_function_level' }) { + $self->{ '_level' } = pop(@{ $self->{ '_level_parenthesis_function' } }) || 0; + $self->_over($token,$last) if (!$self->{ '_is_in_operator' } && !$self->{ '_is_in_alter' }); + } + } + $self->{ '_is_in_function' } = 0 if (!$self->{ '_parenthesis_function_level' }); - #### - # Set open parenthesis position to know if we - # are in subqueries or function parameters - #### - if ( $token eq ')') - { - if (!$self->{ '_is_in_function' }) { - $self->{ '_parenthesis_level' }--; - } else { - $self->{ '_parenthesis_function_level' }--; + if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_sub_query' }) { + $self->{ '_is_in_sub_query' }--; + $self->_back($token, $last); } - $self->{ '_is_in_function' } = 0 if (!$self->{ '_parenthesis_function_level' }); - } - elsif ( $token eq '(') - { - if ($self->{ '_is_in_function' }) { - $self->{ '_parenthesis_function_level' }++; - } else { - if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_from' }) { - push(@{ $self->{ '_level_parenthesis' } } , $self->{ '_level' }); - } - $self->{ '_parenthesis_level' }++; + if ($self->{ '_is_in_value' }) { + $self->{ '_parenthesis_level_value' }-- if ($self->{ '_parenthesis_level_value' }); } - } - - #### - # 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 ')')) + } + elsif ( $token eq '(') + { + $self->{ '_parenthesis_filter_level' }++ if ($self->{ '_is_in_filter' }); + if ($self->{ '_is_in_function' }) { + $self->{ '_parenthesis_function_level' }++; + push(@{ $self->{ '_level_parenthesis_function' } } , $self->{ '_level' }) if ($self->{ '_parenthesis_function_level' } == 1); + } else { + if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_from' }) { + push(@{ $self->{ '_level_parenthesis' } } , $self->{ '_level' }); + } + $self->{ '_parenthesis_level' }++; + if ($self->{ '_is_in_value' }) { + $self->{ '_parenthesis_level_value' }++; + } + } + + if (defined $self->_next_token and $self->_next_token =~ /^(SELECT|WITH)$/i) { + $self->{ '_is_in_sub_query' }++ if (defined $last and uc($last) ne 'AS'); + } + } + + #### + # 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_partition' } && !$self->{ '_is_in_publication' } + && !$self->{ '_is_in_policy' }) { - $self->{ '_is_in_with' } = 1; + $self->{ '_is_in_with' } = 1 if (!$self->{ '_is_in_using' } && uc($self->_next_token) ne 'ORDINALITY' && uc($last) ne 'START'); + $self->{ 'no_break' } = 1 if (uc($self->_next_token) eq 'ORDINALITY'); + } + elsif ($token =~ /^WITH$/i && uc($self->_next_token) eq 'ORDINALITY') + { + $self->{ 'no_break' } = 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); + $self->{ '_is_in_with' }++ if ($self->{ '_is_in_with' } == 1); + } + elsif ($self->{ '_is_in_create' } && $token =~ /^AS$/i && defined $self->_next_token && uc($self->_next_token) eq 'SELECT') + { + $self->{ '_is_in_create' } = 0; + } + elsif ( $token eq '[' ) + { + $self->{ '_is_in_array' }++; + } + elsif ( $token eq ']' ) + { + $self->{ '_is_in_array' }-- if ($self->{ '_is_in_array' }); } - elsif ( $token eq ')' ) + 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->_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; + $self->{ '_has_order_by' } = 0; + if ($self->{ '_is_in_distinct' }) { + $self->_add_token( $token ); + $self->_new_line($token,$last); + $self->{ '_is_in_distinct' } = 0; + $last = $token; + next; } - } - #### - # 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. - 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); - } - } + $self->{ '_is_in_using' } = 0 if ($self->{ '_is_in_using' } && !$self->{ '_parenthesis_level' }); - #### - # 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; + if ($self->{ '_is_in_create' } > 1 and defined $self->_next_token && uc($self->_next_token) eq 'AS' && !$self->{ '_is_in_with'}) { + $self->_new_line($token,$last); + } + if (($self->{ '_is_in_with' } > 1 || $self->{ '_is_in_operator' }) && !$self->{ '_parenthesis_level' } + && !$self->{ '_is_in_alter' } && !$self->{ '_is_in_policy' }) { + $self->_new_line($token,$last) if (!$self->{ '_is_in_operator' } || + (!$self->{ '_is_in_drop' } and $self->_next_token eq ';')); + if (!$self->{ '_is_in_operator' }) { + $self->{ '_level' } = pop(@{ $self->{ '_level_stack' } }) || 0; + $self->_back($token, $last); + } + $self->_add_token( $token ); + if (!$self->{ '_is_in_operator' }) { + @{ $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; + } + } + $last = $token; + next; + } } + elsif (defined $self->_next_token && $self->_next_token eq '(') + { + $self->{ '_is_in_filter' } = 1 if (uc($token) eq 'FILTER'); + $self->{ '_is_in_grouping' } = 1 if ($token =~ /^GROUPING|ROLLUP$/i); + } + elsif ( uc($token) eq 'PASSING' and defined $self->_next_token && uc($self->_next_token) eq 'BY') + { + $self->{ '_has_order_by' } = 1; + } - #### - # 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 - #### - 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 - 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' }) { + # Explain need indentation in option list + if ( uc($token) eq 'EXPLAIN' ) + { + $self->{ '_is_in_explain' } = 1; + } + + #### + # Set the current kind of statement parsed + #### + if ($token =~ /^(FUNCTION|PROCEDURE|SEQUENCE|INSERT|DELETE|UPDATE|SELECT|RAISE|ALTER|GRANT|REVOKE|COMMENT|DROP|RULE|COMMENT)$/i) { + my $k_stmt = uc($1); + $self->{ '_is_in_explain' } = 0; + # 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 ')')) { + if ($k_stmt !~ /^UPDATE|DELETE$/i || !$self->{ '_is_in_create' }) { + $self->{ '_current_sql_stmt' } = $k_stmt if ($self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/i and !$self->{ '_is_in_trigger' } and !$self->{ '_is_in_operator' } && !$self->{ '_is_in_alter' }); + } + } + } + + #### + # Mark that we are in CREATE statement that need newline + # after a comma in the parameter, declare or column lists. + #### + if ($token =~ /^(FUNCTION|PROCEDURE)$/i and $self->{ '_is_in_create' } and !$self->{'_is_in_trigger'}) { + $self->{ '_is_in_create_function' } = 1; + } elsif ($token =~ /^(FUNCTION|PROCEDURE)$/i and $self->{'_is_in_trigger'}) { $self->{ '_is_in_index' } = 1; - } - } - # Desactivate index like formatting when RETURN(S) keyword is found - elsif ($token =~ /^(RETURN|RETURNS)$/i) { - $self->{ '_is_in_index' } = 0; } + if ($token =~ /^CREATE$/i && $self->_next_token !~ /^(EVENT|UNIQUE|INDEX|EXTENSION|TYPE|PUBLICATION|OPERATOR|RULE|CONVERSION|DOMAIN)$/i) { + $self->{ '_is_in_create' } = 1; + } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^RULE$/i) { + $self->{ '_is_in_rule' } = 1; + } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^EVENT$/i) { + $self->{ '_is_in_trigger' } = 1; + } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^TYPE$/i) { + $self->{ '_is_in_type' } = 1; + } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^PUBLICATION$/i) { + $self->{ '_is_in_publication' } = 1; + } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^CONVERSION$/i) { + $self->{ '_is_in_conversion' } = 1; + } elsif ($token =~ /^CREATE|DROP$/i && $self->_next_token =~ /^OPERATOR$/i) { + $self->{ '_is_in_operator' } = 1; + $self->{ '_is_in_drop' } = 1 if ($token =~ /^DROP$/i); + } elsif ($token =~ /^ALTER$/i) { + $self->{ '_is_in_alter' }++; + } elsif ($token =~ /^DROP$/i){ + $self->{ '_is_in_drop' } = 1; + } elsif ($token =~ /^VIEW$/i and $self->{ '_is_in_create' }) { + $self->{ '_is_in_index' } = 1; + $self->{ '_is_in_create' } = 0; + } elsif ($token =~ /^AGGREGATE$/i and $self->{ '_is_in_create' }) { + $self->{ '_is_in_aggregate' } = 1; + $self->{ '_has_order_by' } = 1; + } elsif ($token =~ /^EVENT$/i && $self->_next_token =~ /^TRIGGER$/i) { + $self->_over($token, $last); + $self->{ '_is_in_index' } = 1; + } + + if ($self->{ '_is_in_using' } and defined $self->_next_token and $self->_next_token =~ /^OPERATOR|AS$/i) { + $self->{ '_is_in_using' } = 0; + } + + if ($token =~ /^ALTER$/i and $self->{ '_is_in_alter' } > 1) { + $self->_new_line($token,$last); + $self->_over($token, $last) if ($last ne ','); + $self->_add_token( $token ); + $last = $token; + next; + } #### - # 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. + # Mark that we are in a CALL statement to remove any new line #### - if ($token =~ /^(string_agg|group_concat)$/i) { + if ($token =~ /^CALL/i) { + $self->{ '_is_in_call' } = 1; + } + + # Increment operator tag to add newline in alter operator statement + if (($self->{ '_is_in_alter' } or uc($last) eq 'AS') and uc($token) eq 'OPERATOR') { + $self->_new_line($token,$last) if (uc($last) eq 'AS' and uc($token) eq 'OPERATOR'); + $self->{ '_is_in_operator' }++; + } + + #### + # 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 + #### + 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'); + } elsif ($self->{ '_is_in_create' } and (uc($token) eq 'UNIQUE' or (uc($token) eq 'PRIMARY' and uc($self->_next_token) eq 'KEY'))) { + $self->{ '_is_in_constraint' } = 1; + } + + # Same as above but for ALTER FUNCTION/PROCEDURE/SEQUENCE or when + # we are in a CREATE FUNCTION/PROCEDURE statement + elsif ($token =~ /^(FUNCTION|PROCEDURE|SEQUENCE)$/i and !$self->{'_is_in_trigger'}) { + $self->{ '_is_in_index' } = 1 if (uc($last) eq 'ALTER' and !$self->{ '_is_in_operator' } and !$self->{ '_is_in_alter' }); + if ($token =~ /^FUNCTION$/i && ($self->{ '_is_in_create' } || $self->{ '_current_sql_stmt' } eq 'COMMENT')) { + $self->{ '_is_in_index' } = 1 if (!$self->{ '_is_in_operator' }); + } elsif ($token =~ /^PROCEDURE$/i && $self->{ '_is_in_create' }) { + $self->{ '_is_in_index' } = 1; + $self->{ '_is_in_procedure' } = 1; + } + } + # Desactivate index like formatting when RETURN(S) keyword is found + elsif ($token =~ /^(RETURN|RETURNS)$/i) { + $self->{ '_is_in_index' } = 0; + } elsif ($token =~ /^AS$/i) { + if ( !$self->{ '_is_in_index' } and $self->{ '_is_in_from' } and $last eq ')' and uc($token) eq 'AS' and $self->_next_token() eq '(') { + $self->{ '_is_in_index' } = 1; + } else { + $self->{ '_is_in_index' } = 0; + } + $self->{ '_is_in_block' } = 1 if ($self->{ '_is_in_procedure' }); + $self->{ '_is_in_over' } = 0; + } + if ($token =~ /^BEGIN|DECLARE$/i) { + $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); + } + + #### + # 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|array_agg|percentile_cont)$/i) { + $self->{ '_has_order_by' } = 1; + } elsif ( $token =~ /^(?:GENERATED)$/i and $self->_next_token =~ /^ALWAYS|BY$/i ) { + $self->{ 'no_break' } = 1; + } elsif ( $token =~ /^(?:TRUNCATE)$/i ) { + $self->{ 'no_break' } = 1; + } elsif ( uc($token) eq 'IDENTITY' ) { + $self->{ '_has_order_by' } = 0; + $self->{ 'no_break' } = 0; + } elsif ( $self->{ '_has_order_by' } and uc($token) eq 'ORDER' and $self->_next_token =~ /^BY$/i) { + $self->_add_token( $token, $last ); + $last = $token; + next; + } elsif ($self->{ '_has_order_by' } and uc($token) eq 'BY') { + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ($token =~ /^OVER$/i) + { + $self->_add_token( $token ); + $self->{ '_is_in_over' } = 1; $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 ); - $last = $token; - next; - } elsif ($self->{ '_has_order_by' } and uc($token) eq 'BY') { - $self->_add_token( $token ); - $self->{ '_has_order_by' } = 0; - next; + $last = $token; + next; } - #### - # 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' } || $self->_next_token =~ /CODEPART/) - and $self->{ '_current_sql_stmt' } =~ /^(FUNCTION|PROCEDURE)$/i) - { - if ($self->{ '_is_in_create' }) + # Fix case where we don't knwon if we are outside a SQL function + if (defined $last and uc($last) eq 'AS'and defined $self->_next_token and $self->_next_token eq ';' + and $self->{ '_is_in_create_function' }) { + $self->{ '_is_in_create_function' } = 0; + } + + #### + # 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' } || $self->_next_token =~ /CODEPART/) + and $self->{ '_current_sql_stmt' } =~ /^(FUNCTION|PROCEDURE)$/i) + { + if ($self->{ '_is_in_create' }) { - $self->_new_line; - $self->_add_token( $token ); + $self->_new_line($token,$last); + $self->_add_token( $token ); @{ $self->{ '_level_stack' } } = (); $self->{ '_level' } = 0; $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - $self->{ '_is_in_create' } = 0; + $self->{ '_is_in_create' } = 0; } else { - $self->_add_token( $token ); - } - if ($self->_next_token !~ /CODEPART/) + $self->_add_token( $token ); + } + if ($self->_next_token !~ /CODEPART/ || $self->_next_token =~ /^'/) { - $self->{ '_fct_code_delimiter' } = '1'; + $self->{ '_fct_code_delimiter' } = '1'; } - $self->{ '_is_in_create' } = 0; - next; + $self->{ '_is_in_create' } = 0; + $last = $token; + next; + } + elsif ($token =~ /^INSTEAD$/i and defined $last and uc($last) eq 'DO') + { + $self->_add_token( $token ); + $self->_new_line($token,$last); + $last = $token; + next; + } + elsif ($token =~ /^DO$/i and defined $self->_next_token and $self->_next_token =~ /^INSTEAD$/i) + { + $self->_new_line($token,$last); + $self->_over($token,$last); + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ($token =~ /^DO$/i and !$self->{ '_fct_code_delimiter' } and $self->_next_token =~ /^\$[^\s]*/) + { + $self->{ '_fct_code_delimiter' } = '1'; + $self->{ '_is_in_create_function' } = 1; + $self->_new_line($token,$last) if ($self->{ 'content' } !~ /\n$/s); + $self->_add_token( $token ); + $last = $token; + next; } - # Store function code delimiter - if ($self->{ '_fct_code_delimiter' } eq '1') + # Store function code delimiter + if ($self->{ '_fct_code_delimiter' } eq '1') { if ($self->_next_token =~ /CODEPART/) { - $self->{ '_fct_code_delimiter' } = '0'; + $self->{ '_fct_code_delimiter' } = '0'; } else { - $self->{ '_fct_code_delimiter' } = $token; + $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' }) + $self->_add_token( $token ); + $last = $token; + $self->_new_line($token,$last); + $self->_over($token,$last) 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' }) { - $self->{ '_is_in_block' } = -1; - @{ $self->{ '_level_stack' } } = (); - $self->{ '_level' } = 0; - $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - $self->{ '_fct_code_delimiter' } = ''; - $self->{ '_current_sql_stmt' } = ''; - $self->_new_line; - $self->_add_token( $token ); - next; - } - - #### - # 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' ) + $self->{ '_is_in_block' } = -1; + $self->{ '_is_in_exception' } = 0; + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + $self->{ '_fct_code_delimiter' } = ''; + $self->{ '_current_sql_stmt' } = ''; + $self->_new_line($token,$last); + $self->_add_token( $token ); + $last = $token; + next; + } + + #### + # 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' and $self->{ '_is_in_create_function' }) { + $self->{ '_is_in_block' } = -1; + $self->{ '_is_in_exception' } = 0; + $self->{ '_is_in_declare' } = 1; + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + $self->_new_line($token,$last); + $self->_add_token( $token ); + $self->_new_line($token,$last); + $self->_over($token,$last); + $last = $token; + $self->{ '_is_in_create_function' } = 0; + next; + } + elsif ( uc($token) eq 'BEGIN' ) { + $self->{ '_is_in_declare' } = 0; + if ($self->{ '_is_in_block' } == -1) { + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + } else { + # Store current indent position to print END at the right level + push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; + } + $self->_new_line($token,$last); + $self->_add_token( $token ); + if (defined $self->_next_token && $self->_next_token !~ /^(WORK|TRANSACTION|ISOLATION|;)$/i) { + $self->_new_line($token,$last); + $self->_over($token,$last); + $self->{ '_is_in_block' }++; + } + $self->{ '_is_in_work' } = 1 if (defined $self->_next_token && $self->_next_token =~ /^(WORK|TRANSACTION|ISOLATION|;)$/i); + $last = $token; + next; + } + elsif ( $token =~ /^(COMMIT|ROLLBACK)$/i and (not defined $last or uc($last) ne 'ON')) + { + $self->{ '_is_in_work' } = 0; $self->{ '_is_in_declare' } = 0; - if ($self->{ '_is_in_block' } == -1) { - @{ $self->{ '_level_stack' } } = (); - $self->{ '_level' } = 0; - $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - } else { - # Store current indent position to print END at the right level - push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; - } - $self->_new_line; - $self->_add_token( $token ); - if (defined $self->_next_token && $self->_next_token ne ';') { - $self->_new_line; - $self->_over; - $self->{ '_is_in_block' }++; - } - 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 '(') + $self->_new_line($token,$last); + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ( $token =~ /^FETCH$/i and defined $last and $last eq ';') { - $self->_new_line; - } - # and before RETURNS with increasing indent level + $self->_new_line($token,$last); + $self->_back($token, $last) if ($self->{ '_is_in_block' } == -1); + $self->_add_token( $token ); + $last = $token; + $self->{ '_is_in_fetch' } = 1; + 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 '(') + { + $self->_new_line($token,$last); + } + # and before RETURNS with increasing indent level elsif (uc($token) eq 'RETURNS') { - $self->_new_line; - $self->_over; - } + $self->_new_line($token,$last); + $self->_over($token,$last); + } + # and before WINDOW + elsif (uc($token) eq 'WINDOW') + { + $self->_new_line($token,$last); + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + $self->_add_token( $token ); + $last = $token; + $self->{ '_has_order_by' } = 1; + next; + } - if ( $rule ) + # Treated DISTINCT as a modifier of the whole select clause, not only the first column only + if (uc($token) eq 'ON' && defined $last && uc($last) eq 'DISTINCT') { - $self->_process_rule( $rule, $token ); - } + $self->{ '_is_in_distinct' } = 1; + $self->_over($token,$last); + } + elsif (uc($token) eq 'DISTINCT' && defined $last && uc($last) eq 'SELECT' && defined $self->_next_token && $self->_next_token !~ /^ON$/i) + { + $self->_add_token( $token ); + $self->_new_line($token,$last) if (!$self->{'wrap_after'}); + $self->_over($token,$last); + $last = $token; + next; + } - elsif ($token =~ /^(LANGUAGE|SECURITY|COST)$/i) + if ( $rule ) { - $self->_new_line; - $self->_add_token( $token ); - } + $self->_process_rule( $rule, $token ); + } - elsif ( $token eq '(' ) + elsif ($token =~ /^(LANGUAGE|SECURITY|COST)$/i && !$self->{ '_is_in_alter' } && !$self->{ '_is_in_drop' } ) { - $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) { - $self->_over; - $self->_new_line; - next; - } - $self->_over; - if ($self->{ '_is_in_type' } == 1) { - $last = $token; - next; - } + $self->_new_line($token,$last) if (uc($token) ne 'SECURITY' or (defined $last and uc($last) ne 'LEVEL')); + $self->_add_token( $token ); + } + elsif ($token =~ /^PARTITION$/i && !$self->{ '_is_in_over' } && defined $last && $last ne '(') + { + $self->{ '_is_in_partition' } = 1; + if ($self->{ '_is_in_create' } && defined $last and $last eq ')') + { + $self->_new_line($token,$last); + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + $self->_add_token( $token ); } - } - - elsif ( $token eq ')' ) { - if ($self->{ '_is_in_with' } == 1) { - $self->_back; - $self->_new_line; - $self->_add_token( $token ); - next; + else + { + $self->_add_token( $token ); + } + } + elsif ($token =~ /^POLICY$/i) + { + $self->{ '_is_in_policy' } = 1; + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ($token =~ /^TRIGGER$/i and defined $last and uc($last) eq 'CREATE') + { + $self->{ '_is_in_trigger' } = 1; + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ($token =~ /^(BEFORE|AFTER)$/i and $self->{ '_is_in_trigger' }) + { + $self->_new_line($token,$last); + $self->_over($token,$last); + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ($token =~ /^EXECUTE$/i and ($self->{ '_is_in_trigger' } or (defined $last and uc($last) eq 'AS'))) + { + $self->_new_line($token,$last); + $self->_add_token( $token ); + $last = $token; + next; + } + elsif ( $token eq '(' ) + { + if ($self->{ '_is_in_aggregate' } && defined $self->_next_token and ($self->_is_keyword($self->_next_token) or $self->_is_sql_keyword($self->_next_token)) and uc($self->_next_token) ne 'VARIADIC') { + $self->{ '_is_in_aggregate' } = 0; + $self->{ '_has_order_by' } = 0; } - if ($self->{ '_is_in_index' }) { - $self->_add_token( '' ); - $self->_add_token( $token ); - $last = $token; - next; + $self->{ '_is_in_create' }++ if ($self->{ '_is_in_create' }); + $self->{ '_is_in_constraint' }++ if ($self->{ '_is_in_constraint' }); + $self->_add_token( $token, $last ); + if (defined $self->_next_token and $self->_next_token eq ')' and !$self->{ '_is_in_create' }) { + $last = $token; + next; } - $self->_new_line if ($self->{ '_is_in_create' } > 1 - and (not defined $self->_next_token or $self->_next_token eq ';') - ); - $self->_new_line if ($self->{ '_is_in_type' } == 1 - and (not defined $self->_next_token or $self->_next_token eq ';') - ); - $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->_back; - $self->_add_token( $token ); - # Do not go further if this is the last token - if (not defined $self->_next_token) { - $last = $token; - next; + if ( !$self->{ '_is_in_index' } && !$self->{ '_is_in_publication' } + && !$self->{ '_is_in_distinct' } && !$self->{ '_is_in_filter' } + && !$self->{ '_is_in_grouping' } && !$self->{ '_is_in_partition' } + && !$self->{ '_is_in_over' } && !$self->{ '_is_in_trigger' } + && !$self->{ '_is_in_policy' } && !$self->{ '_is_in_aggregate' } + && !$self->{ 'no_break' } + ) { + if (uc($last) eq 'AS' || $self->{ '_is_in_create' } == 2 || uc($self->_next_token) eq 'CASE') + { + $self->_new_line($token,$last) if ((!$self->{'_is_in_function'} or $self->_next_token =~ /^CASE$/i) and $self->_next_token ne ')' and $self->_next_token !~ /^(PARTITION|ORDER)$/i); + } + if ($self->{ '_is_in_with' } == 1 or $self->{ '_is_in_explain' }) { + $self->_over($token,$last); + $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); + $last = $token if (!$self->{ '_is_in_explain' } || $self->{ 'wrap_after' }); + next; + } + if (!$self->{ '_is_in_if' } and !$self->{ '_is_in_alter' } and (!$self->{ '_is_in_function' } or $last ne '(')) + { + $self->_over($token,$last) if ($self->{ '_is_in_operator' } <= 2); + if (!$self->{ '_is_in_function' } and !$self->_is_type($self->_next_token)) { + if ($self->{ '_is_in_operator' } == 1) { + $self->_new_line($token,$last); + $self->{ '_is_in_operator' }++; + } elsif ($self->{ '_is_in_type' }) { + $self->_new_line($token,$last); + } + } + $last = $token; + } + if ($self->{ '_is_in_type' } == 1) { + $last = $token; + next; + } + } + + if ($self->{ 'format_type' } && $self->{ '_current_sql_stmt' } =~ /FUNCTION|PROCEDURE/i + && $self->{ '_is_in_create' } == 2 + && (not defined $self->_next_token or $self->_next_token ne ')') + ) { + $self->_over($token,$last) if ($self->{ '_is_in_block' } < 0); + $self->_new_line($token,$last); + next; } + } - # When closing CTE statement go back again - if ($self->_next_token =~ /^SELECT|INSERT|UPDATE|DELETE$/i) { - $self->_back; + elsif ( $token eq ')' ) + { + if ($self->{ '_is_in_constraint' } and defined $self->_next_token + and ($self->_next_token eq ',' or $self->_next_token eq ')')) { + $self->{ '_is_in_constraint' } = 0; } - if ($self->{ '_is_in_create' } <= 1) { - my $next_tok = quotemeta($self->_next_token); - $self->_new_line - if (defined $self->_next_token - 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 !~ /^:/ - and $self->_next_token ne ';' - and $self->_next_token ne ',' - and $self->_next_token ne '||' - and ($self->_is_keyword($self->_next_token) or $self->_is_function($self->_next_token)) - and !exists $self->{ 'dict' }->{ 'symbols' }{ $next_tok } - ); + if ($self->{ '_is_in_with' } == 1 || $self->{ '_is_in_explain' }) { + $self->_back($token, $last); + $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); + $self->_add_token( $token ); + $self->{ '_is_in_explain' } = 0; + next; + } + if ($self->{ 'format_type' } && $self->{ '_current_sql_stmt' } =~ /FUNCTION|PROCEDURE/i + && $self->{ '_is_in_create' } == 2 + ) { + $self->_back($token, $last) if ($self->{ '_is_in_block' } < 0); + $self->_new_line($token,$last) if (defined $last && $last ne '('); } - } + if ($self->{ '_is_in_index' } || $self->{ '_is_in_alter' } + || $self->{ '_is_in_partition' } || $self->{ '_is_in_policy' } + || (defined $self->_next_token and $self->_next_token =~ /^OVER$/i) + ) { + $self->_add_token( '' ); + $self->_add_token( $token ); + $self->{ '_is_in_over' } = 0 if (!$self->{ '_parenthesis_level' }); + $last = $token; + $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); + next; + } + if (defined $self->_next_token && $self->_next_token !~ /FILTER/i) + { - elsif ( $token eq ',' ) { - my $add_newline = 0; - $add_newline = 1 if ( !$self->{ 'no_break' } - && !$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$/ + my $add_nl = 0; + $add_nl = 1 if ($self->{ '_is_in_create' } > 1 + and defined $last and $last ne '(' + and (not defined $self->_next_token or $self->_next_token =~ /^PARTITION|;$/i or ($self->_next_token =~ /^ON$/i and !$self->{ '_parenthesis_level' })) + ); + $add_nl = 1 if ($self->{ '_is_in_type' } == 1 + and $self->_next_token !~ /^AS$/i + and (not defined $self->_next_token or $self->_next_token eq ';') + ); + $add_nl = 1 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 ($self->{ '_is_in_create' } or $last ne ')' and $last ne ']') + ); + $self->_new_line($token,$last) if ($add_nl); + $self->_back($token, $last) if (!$self->{ '_is_in_grouping' } + && !$self->{ '_is_in_trigger' } && !$self->{ 'no_break' } + ); + $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); + $self->{ '_is_in_type' }-- if ($self->{ '_is_in_type' }); + } + if (!$self->{ '_parenthesis_level' }) { + $self->{ '_is_in_filter' } = 0; + $self->{ '_is_in_within' } = 0; + $self->{ '_is_in_grouping' } = 0; + $self->{ '_is_in_over' } = 0; + $self->{ '_has_order_by' } = 0; + $self->{ '_is_in_policy' } = 0; + $self->{ '_is_in_where' } = 0; + $self->{ '_is_in_aggregate' } = 0; + } + $self->_add_token( $token ); + # Do not go further if this is the last token + if (not defined $self->_next_token) { + $last = $token; + next; + } + + # When closing CTE statement go back again + if ($self->_next_token =~ /^SELECT|INSERT|UPDATE|DELETE$/i && !$self->{ '_is_in_policy' }) { + $self->_back($token, $last); + } + if ($self->{ '_is_in_create' } <= 1) { + my $next_tok = quotemeta($self->_next_token); + $self->_new_line($token,$last) + if (defined $self->_next_token + and $self->_next_token !~ /^AS|IS|THEN|INTO|BETWEEN|ON|FILTER|WITHIN$/i + and ($self->_next_token !~ /^AND|OR$/i or !$self->{ '_is_in_if' }) + and $self->_next_token ne ')' + and $self->_next_token !~ /^:/ + and $self->_next_token ne ';' + and $self->_next_token ne ',' + and $self->_next_token ne '||' + and ($self->_is_keyword($self->_next_token) or $self->_is_function($self->_next_token)) + and $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ + and !exists $self->{ 'dict' }->{ 'symbols' }{ $next_tok } + ); + } + } + + elsif ( $token eq ',' ) + { + my $add_newline = 0; + $self->{ '_is_in_constraint' } = 0 if ($self->{ '_is_in_constraint' } == 1); + $self->{ '_col_count' }++ if (!$self->{ '_is_in_function' }); + if (($self->{ '_is_in_over' } or $self->{ '_has_order_by' }) and !$self->{ '_parenthesis_level' } and !$self->{ '_parenthesis_function_level' }) + { + $self->{ '_is_in_over' } = 0; + $self->{ '_has_order_by' } = 0; + $self->_back($token, $last); + } + $add_newline = 1 if ( !$self->{ 'no_break' } + && !$self->{ '_is_in_function' } + && !$self->{ '_is_in_distinct' } + && !$self->{ '_is_in_array' } + && ($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->{ '_is_in_where' } - && !$self->{ '_is_in_index' } - && $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ - && $self->_next_token !~ /^('$|\-\-)/i - && !$self->{ '_parenthesis_function_level' } - ); - - if ($self->{ '_is_in_with' } >= 1 && !$self->{ '_parenthesis_level' }) { - $add_newline = 1; + && !$self->{ '_is_in_where' } + && !$self->{ '_is_in_drop' } + && !$self->{ '_is_in_index' } + && !$self->{ '_is_in_aggregate' } + && !$self->{ '_is_in_alter' } + && !$self->{ '_is_in_publication' } + && !$self->{ '_is_in_call' } + && !$self->{ '_is_in_policy' } + && !$self->{ '_is_in_grouping' } + && !$self->{ '_is_in_partition' } + && ($self->{ '_is_in_constraint' } <= 1) + && $self->{ '_is_in_operator' } != 1 + && !$self->{ '_has_order_by' } + && $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ + && $self->_next_token !~ /^('$|\s*\-\-)/i + && !$self->{ '_parenthesis_function_level' } + && (!$self->{ '_col_count' } or $self->{ '_col_count' } > ($self->{ 'wrap_after' } - 1)) + || ($self->{ '_is_in_with' } and !$self->{ 'wrap_after' }) + ); + $self->{ '_col_count' } = 0 if ($self->{ '_col_count' } > ($self->{ 'wrap_after' } - 1)); + $add_newline = 0 if ($self->{ '_is_in_using' } and $self->{ '_parenthesis_level' }); + $add_newline = 0 if ($self->{ 'no_break' }); + + if ($self->{ '_is_in_with' } >= 1 && !$self->{ '_parenthesis_level' }) { + $add_newline = 1 if (!$self->{ 'wrap_after' }); + } + if ($self->{ 'format_type' } && $self->{ '_current_sql_stmt' } =~ /FUNCTION|PROCEDURE/i && $self->{ '_is_in_create' } == 2) { + $add_newline = 1; } - $self->_new_line if ($add_newline && $self->{ 'comma' } eq 'start'); - $self->_add_token( $token ); - $self->_new_line if ($add_newline && $self->{ 'comma' } eq 'end'); - } + if ($self->{ '_is_in_alter' } && $self->{ '_is_in_operator' } >= 2) { + $add_newline = 1 if (defined $self->_next_token and $self->_next_token =~ /^OPERATOR|FUNCTION$/i); + } + $self->_new_line($token,$last) if ($add_newline && $self->{ 'comma' } eq 'start'); + $self->_add_token( $token ); + $add_newline = 0 if ($self->{ '_is_in_value' } and $self->{ '_parenthesis_level_value' }); + $self->_new_line($token,$last) if ($add_newline && $self->{ 'comma' } eq 'end'); + } - 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 - $self->{ '_has_from' } = 0; - $self->{ '_is_in_where' } = 0; - $self->{ '_is_in_from' } = 0; - $self->{ '_is_in_join' } = 0; - $self->{ '_is_in_create' } = 0; - $self->{ '_is_in_type' } = 0; - $self->{ '_is_in_function' } = 0; - $self->{ '_is_in_index' } = 0; - $self->{ '_is_in_if' } = 0; - $self->{ '_current_sql_stmt' } = ''; - $self->{ '_is_in_with' } = 0; - $self->{ '_has_order_by' } = 0; - $self->{ '_has_over_in_join' } = 0; - $self->{ '_parenthesis_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' }) - { - $self->{ '_new_line' } = 0; - $self->_new_line; + elsif ( $token eq ';' or $token =~ /^\\(?:g|crosstabview|watch)/ ) { # statement separator or executing psql meta command (prefix 'g' includes all its variants) + + $self->_add_token($token); + + if ($self->{ '_is_in_rule' }) { + $self->_back($token, $last); } - # 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) + elsif ($self->{ '_is_in_create' } && $self->{ '_is_in_block' } > -1) { - @{ $self->{ '_level_stack' } } = (); - $self->{ '_level' } = 0; - $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; } - else + + # Initialize most of statement related variables + $self->{ 'no_break' } = 0; + $self->{ '_is_in_where' } = 0; + $self->{ '_is_in_from' } = 0; + $self->{ '_is_in_join' } = 0; + $self->{ '_is_in_create' } = 0; + $self->{ '_is_in_alter' } = 0; + $self->{ '_is_in_rule' } = 0; + $self->{ '_is_in_publication' } = 0; + $self->{ '_is_in_call' } = 0; + $self->{ '_is_in_type' } = 0; + $self->{ '_is_in_function' } = 0; + $self->{ '_is_in_prodedure' } = 0; + $self->{ '_is_in_index' } = 0; + $self->{ '_is_in_if' } = 0; + $self->{ '_is_in_with' } = 0; + $self->{ '_has_order_by' } = 0; + $self->{ '_has_over_in_join' } = 0; + $self->{ '_parenthesis_level' } = 0; + $self->{ '_parenthesis_function_level' } = 0; + $self->{ '_is_in_constraint' } = 0; + $self->{ '_is_in_distinct' } = 0; + $self->{ '_is_in_array' } = 0; + $self->{ '_is_in_filter' } = 0; + $self->{ '_parenthesis_filter_level' } = 0; + $self->{ '_is_in_partition' } = 0; + $self->{ '_is_in_over' } = 0; + $self->{ '_is_in_policy' } = 0; + $self->{ '_is_in_trigger' } = 0; + $self->{ '_is_in_using' } = 0; + $self->{ '_and_level' } = 0; + $self->{ '_col_count' } = 0; + $self->{ '_is_in_drop' } = 0; + $self->{ '_is_in_conversion' } = 0; + $self->{ '_is_in_operator' } = 0; + $self->{ '_is_in_explain' } = 0; + $self->{ '_is_in_sub_query' } = 0; + $self->{ '_is_in_fetch' } = 0; + $self->{ '_is_in_aggregate' } = 0; + $self->{ '_is_in_value' } = 0; + $self->{ '_parenthesis_level_value' } = 0; + + if ( $self->{ '_insert_values' } ) { - if ($#{ $self->{ '_level_stack' } } == -1) { - $self->{ '_level' } = ($self->{ '_is_in_declare' }) ? 1 : ($self->{ '_is_in_block' }+1); - } else { - $self->{ '_level' } = $self->{ '_level_stack' }[-1]; + if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' }) + { + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); } + elsif ($self->{ '_is_in_block' } == -1 and $self->{ '_current_sql_stmt' } eq 'INSERT' and !$self->{ '_is_in_create' } and !$self->{ '_is_in_create_function' }) + { + $self->_back($token, $last); + pop( @{ $self->{ '_level_stack' } } ); + } + else + { + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + } + $self->{ '_insert_values' } = 0; } - } - elsif ($token =~ /^FOR$/i) + $self->{ '_current_sql_stmt' } = ''; + $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); + $self->_new_line($token,$last); + # Add an additional newline after ; when we are not in a function + if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_work' } + and !$self->{ '_is_in_declare' }) + #and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' }) + { + $self->{ '_new_line' } = 0; + $self->_new_line($token,$last); + } + # 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) + { + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + } + elsif (not defined $self->_next_token or $self->_next_token !~ /^INSERT$/) + { + if ($#{ $self->{ '_level_stack' } } == -1) { + $self->{ '_level' } = ($self->{ '_is_in_declare' }) ? 1 : ($self->{ '_is_in_block' }+1); + } else { + $self->{ '_level' } = $self->{ '_level_stack' }[-1]; + } + } + $last = $token; + } + elsif ($token =~ /^FOR$/i && (!$self->{ '_is_in_policy' } || $self->{ 'format_type' })) { - if ($self->_next_token =~ /^(UPDATE|KEY|NO)$/i) + if ($self->_next_token =~ /^(UPDATE|KEY|NO|VALUES)$/i) { - $self->_back; - $self->_new_line; + $self->_back($token, $last); + $self->_new_line($token,$last); + } + elsif ($self->_next_token =~ /^EACH$/ and $self->{ '_is_in_trigger' }) + { + $self->_new_line($token,$last); } - $self->_add_token( $token ); - if ($self->_next_token =~ /^SELECT$/i) + if (!$self->{ 'format_type' }) { - $self->_new_line; - $self->_over; + $self->_add_token( $token ); + # cover FOR in cursor + $self->_over($token,$last) if (uc($self->_next_token) eq 'SELECT' && !$self->{ '_is_in_policy' } && !$self->{ '_is_in_publication' }); + $last = $token; + next; } - } + if ($self->_next_token =~ /^SELECT|UPDATE|DELETE|INSERT|TABLE|ALL$/i) + { + # cover FOR in cursor and in policy or publication statements + $self->_over($token,$last) if (uc($self->_next_token) eq 'SELECT' || $self->{ '_is_in_policy' } || $self->{ '_is_in_publication' }); + } + if ($self->{ 'format_type' } && $self->{ '_is_in_policy' } || $self->{ '_is_in_publication' }) { + $self->_new_line($token,$last); + } + $self->_add_token( $token ); + $last = $token; + next; + } - elsif ( $token =~ /^(?:FROM|WHERE|SET|RETURNING|HAVING|VALUES)$/i ) + elsif ( $token =~ /^(?:FROM|WHERE|SET|RETURNING|HAVING|VALUES)$/i ) { + if (uc($token) eq 'FROM' and $self->{ '_has_order_by' } and !$self->{ '_parenthesis_level' }) + { + $self->_back($token, $last) if ($self->{ '_has_order_by' }); + } $self->{ 'no_break' } = 0; - - if (uc($last) eq 'DISTINCT' and $token =~ /^FROM$/i) + $self->{ '_col_count' } = 0; + # special cases for create partition statement + if ($token =~ /^VALUES$/i && defined $last and $last =~ /^FOR|IN$/i) { $self->_add_token( $token ); + $self->{ 'no_break' } = 1; $last = $token; next; } - if (($token =~ /^FROM$/i) && $self->{ '_has_from' } && !$self->{ '_is_in_function' }) + elsif ($token =~ /^FROM$/i && defined $last and uc($last) eq 'VALUES') { - $self->{ '_has_from' } = 0; + $self->_add_token( $token ); + $last = $token; + next; } - if ($token =~ /^FROM$/i) + + # Case of DISTINCT FROM clause + if ($token =~ /^FROM$/i) { - $self->{ '_is_in_from' }++ if (!$self->{ '_is_in_function' }); + if (uc($last) eq 'DISTINCT' || $self->{ '_is_in_fetch' } || $self->{ '_is_in_alter' } || $self->{ '_is_in_conversion' }) + { + $self->_add_token( $token ); + $last = $token; + next; + } } - if ($token =~ /^WHERE$/i) + + if ($token =~ /^FROM$/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; - } + $self->{ '_is_in_from' }++ if (!$self->{ '_is_in_function' } && !$self->{ '_is_in_partition' }); + } + + if ($token =~ /^WHERE$/i && !$self->{ '_is_in_filter' }) + { + $self->_back($token, $last) 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_where' }-- if ($self->{ '_is_in_where' }); - } + $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; - } + # Add newline before SET statement in function header + $self->_new_line($token,$last) if (not defined $last or $last !~ /DELETE|UPDATE/i); + } 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; - next; - } + $self->_new_line($token,$last); + $self->_add_token( $token ); + $self->_over($token,$last); + $last = $token; + $self->{ '_is_in_join' } = 0; + $last = $token; + next; + } elsif ($token !~ /^FROM$/i or (!$self->{ '_is_in_function' } - and $self->{ '_current_sql_stmt' } ne 'DELETE')) + and $self->{ '_current_sql_stmt' } !~ /DELETE|REVOKE/)) { - if ($token !~ /^SET$/i or !$self->{ '_is_in_index' }) + if (!$self->{ '_is_in_filter' } and ($token !~ /^SET$/i or !$self->{ '_is_in_index' })) { - $self->_back; - $self->_new_line; - } - } + $self->_back($token, $last); + $self->_new_line($token,$last) if (!$self->{ '_is_in_rule' }); + } + } else { - $self->_add_token( $token ); - $last = $token; - next; - } - if ($token =~ /^VALUES$/i and ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(')) + $self->_add_token( $token ); + $last = $token; + next; + } + + if ($token =~ /^VALUES$/i and !$self->{ '_is_in_rule' } and ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(')) { - $self->_over; + $self->_over($token,$last); + if ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(') { + $self->{ '_insert_values' } = 1; + push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; + } } - $self->_add_token( $token ); + if ($token =~ /^VALUES$/i and $last eq '(') + { + $self->{ '_is_in_value' } = 1; + } + + if (uc($token) eq 'WHERE') { - $self->_over; - } - elsif ( $token =~ /^SET$/i && $self->{ '_current_sql_stmt' } eq 'UPDATE' ) + $self->_add_token( $token, $last ); + $self->{ '_is_in_value' } = 0; + $self->{ '_parenthesis_level_value' } = 0; + } + else { - $self->_new_line; - $self->_over; - } - elsif ( $token !~ /^SET$/i || $self->{ '_current_sql_stmt' } eq 'UPDATE' ) + $self->_add_token( $token ); + } + + if ($token =~ /^VALUES$/i and $last eq '(') { - if (defined $self->_next_token and $self->_next_token ne '(' + $self->_over($token,$last); + } + elsif ( $token =~ /^SET$/i && $self->{ '_current_sql_stmt' } eq 'UPDATE' ) + { + $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); + $self->_over($token,$last); + } + elsif ( !$self->{ '_is_in_over' } and !$self->{ '_is_in_filter' } and ($token !~ /^SET$/i or $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') + $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); + $self->_over($token,$last); + } + } + } + + # 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') { - $self->_new_line; - $self->_add_token( $token ); - } - - elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ - and $token =~ /^(?:SELECT|PERFORM|UPDATE|DELETE)$/i ) + $self->_new_line($token,$last); + $self->_add_token( $token ); + } + + elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ + and $token =~ /^(?:SELECT|PERFORM|UPDATE|DELETE)$/i + and (!$self->{ '_is_in_policy' } || $self->{ 'format_type' }) + ) { - $self->{ 'no_break' } = 0; + $self->{ 'no_break' } = 0; - if ($token =~ /^UPDATE$/i and $last =~ /^(FOR|KEY)$/i) + if ($token =~ /^SELECT|UPDATE|DELETE|INSERT$/i && $self->{ '_is_in_policy' } && $self->{ 'format_type' }) { - $self->_add_token( $token ); + $self->_over($token,$last); } - elsif ($token !~ /^DELETE$/i) + + # case of ON DELETE/UPDATE clause in create table statements + if ($token =~ /^UPDATE|DELETE$/i && $self->{ '_is_in_create' }) { + $self->_add_token( $token ); + $last = $token; + next; + } + if ($token =~ /^UPDATE$/i and $last =~ /^(FOR|KEY)$/i) { - $self->_new_line; - $self->_add_token( $token ); - $self->_new_line; - $self->_over; - } + $self->_add_token( $token ); + } + elsif (!$self->{ '_is_in_policy' } && $token !~ /^DELETE$/i && (!defined $self->_next_token || $self->_next_token !~ /^DISTINCT$/i)) + { + $self->_new_line($token,$last); + $self->_add_token( $token ); + $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); + $self->_over($token,$last); + } else { - $self->_add_token( $token ); - } - } + $self->_add_token( $token ); + } + } - elsif ( $token =~ /^(?:GROUP|ORDER|LIMIT|EXCEPTION)$/i ) + elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ + and uc($token) eq 'INSERT' + and $self->{ '_is_in_policy' } && $self->{ 'format_type' }) { - $self->{ '_is_in_join' } = 0; - if ($token !~ /^EXCEPTION$/i) { - $self->_back; - } else { - $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; - } - $self->_new_line; - $self->_add_token( $token ); - # Store current indent position to print END at the right level - if ($token =~ /^EXCEPTION$/i) - { - push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; - $self->_over; - } - $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); + $self->_add_token( $token ); + $self->_new_line($token,$last); + $self->_over($token,$last); } - - elsif ( $token =~ /^(?:BY)$/i and $last !~ /^(INCREMENT|OWNED)$/ ) + elsif ( $token =~ /^(?:WITHIN)$/i ) { - $self->_add_token( $token ); - $self->_new_line; - $self->_over; + $self->{ '_is_in_within' } = 1; + $self->{ '_has_order_by' } = 1; + $self->_add_token( $token ); + $last = $token; + next; } - elsif ( $token =~ /^(?:CASE)$/i ) + elsif ( $token =~ /^(?:GROUP|ORDER|LIMIT|EXCEPTION)$/i ) { - $self->_add_token( $token ); - # Store current indent position to print END at the right level - push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; - # Mark next WHEN statement as first element of a case - # to force indentation only after this element - $self->{ '_first_when_in_case' } = 1; - $self->{ '_is_in_case' }++; - } - - elsif ( $token =~ /^(?:WHEN)$/i ) + $self->{ '_is_in_value' } = 0; + $self->{ '_parenthesis_level_value' } = 0; + if (uc($token) eq 'GROUP' and !defined $last or uc($last) eq 'EXCLUDE') { + $self->_add_token( $token ); + $last = $token; + next; + } + if (($self->{ '_is_in_within' } && uc($token) eq 'GROUP') || ($self->{ '_is_in_over' } && uc($token) eq 'ORDER')) { + $self->_add_token( $token ); + $last = $token; + next; + } + if ($self->{ '_has_over_in_join' } and uc($token) eq 'GROUP') + { + $self->_back($token, $last); + $self->{ '_has_over_in_join' } = 0; + } + $self->{ '_is_in_join' } = 0; + if ($token !~ /^EXCEPTION$/i) { + $self->_back($token, $last); + } else { + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + } + if (uc($token) ne 'EXCEPTION' or not defined $last or uc($last) ne 'RAISE') + { + # Excluding CREATE/DROP GROUP + $self->_new_line($token,$last) if (not defined $last or $last !~ /^(CREATE|DROP)$/); + } + $self->_add_token( $token ); + # Store current indent position to print END at the right level + if ($token =~ /^EXCEPTION$/i) + { + push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; + $self->_over($token,$last); + $self->{ '_is_in_exception' } = 1; + } + $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); + } + + elsif ( $token =~ /^(?:BY)$/i and $last !~ /^(?:INCREMENT|OWNED|PARTITION)$/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->_over; - $self->{ '_first_when_in_case' } = 0; - } + $self->_add_token( $token ); + $self->{ '_col_count' } = 0 if (defined $last && $last =~ /^(?:GROUP|ORDER)/i); + if (!$self->{ '_has_order_by' }) { + $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); + $self->_over($token,$last); + } + } - elsif ( $token =~ /^(?:IF|LOOP)$/i ) + elsif ( $token =~ /^(?:CASE)$/i ) { - $self->_add_token( $token ); - if (defined $self->_next_token and $self->_next_token ne ';') + $self->_add_token( $token ); + # Store current indent position to print END at the right level + push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; + # Mark next WHEN statement as first element of a case + # to force indentation only after this element + $self->{ '_first_when_in_case' } = 1; + $self->{ '_is_in_case' }++; + } + + elsif ( $token =~ /^(?:WHEN)$/i) + { + if (!$self->{ '_first_when_in_case' } and !$self->{'_is_in_trigger'} + and defined $last and uc($last) ne 'CASE' + # handle case where there is a comment between EXCEPTION and WHEN keywords + and !$self->{ '_is_in_exception' } + ) { - $self->_new_line if ($token =~ /^LOOP$/i); - $self->_over; - push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; - if ($token =~ /^IF$/i) { - $self->{ '_is_in_if' } = 1; - } + $self->{ '_level' } = $self->{ '_level_stack' }[-1]; + $self->{ '_is_in_exception' } = 0; } - } + $self->_new_line($token,$last) if (not defined $last or uc($last) ne 'CASE'); + $self->_add_token( $token ); + if (!$self->{ '_is_in_case' } && !$self->{ '_is_in_trigger' }) { + $self->_over($token,$last); + } + $self->{ '_first_when_in_case' } = 0; + } - elsif ($token =~ /^THEN$/i) + elsif ( $token =~ /^(?:IF|LOOP)$/i && $self->{ '_current_sql_stmt' } ne 'GRANT') { - $self->_add_token( $token ); - $self->_new_line; - $self->{ '_is_in_if' } = 0; - } - - elsif ( $token =~ /^(?:ELSE|ELSIF)$/i ) + $self->_add_token( $token ); + $self->{ 'no_break' } = 0; + if (defined $self->_next_token and $self->_next_token !~ /^(EXISTS|;)$/i) + { + $self->_new_line($token,$last) if ($token =~ /^LOOP$/i); + $self->_over($token,$last); + push @{ $self->{ '_level_stack' } }, $self->{ '_level' }; + if ($token =~ /^IF$/i) { + $self->{ '_is_in_if' } = 1; + } + } + } + + elsif ($token =~ /^THEN$/i) { - $self->_back; - $self->_new_line; - $self->_add_token( $token ); - $self->_new_line if ($token !~ /^ELSIF$/i); - $self->_over; - } + $self->_add_token( $token ); + $self->_new_line($token,$last); + $self->{ '_level' } = $self->{ '_level_stack' }[-1] if ($self->{ '_is_in_if' }); + if ($self->{ '_is_in_case' } && defined $self->_next_token() and $self->_next_token() !~ /^(\(|RAISE)$/i) { + $self->{ '_level' } = $self->{ '_level_stack' }[-1]; + $self->_over($token,$last); + } + $self->{ '_is_in_if' } = 0; + } - elsif ( $token =~ /^(?:END)$/i ) + elsif ( $token =~ /^(?:ELSE|ELSIF)$/i ) + { + $self->_back($token, $last); + $self->_new_line($token,$last); + $self->_add_token( $token ); + $self->_new_line($token,$last) if ($token !~ /^ELSIF$/i); + $self->_over($token,$last); + } + + elsif ( $token =~ /^(?:END)$/i ) { - $self->{ '_first_when_in_case' } = 0; - if ($self->{ '_is_in_case' }) + $self->{ '_first_when_in_case' } = 0; + 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) + $self->{ '_is_in_case' }--; + $self->_back($token, $last); + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + } + # 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) { - # 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) + # 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) { - @{ $self->{ '_level_stack' } } = (); - $self->{ '_level' } = 0; - $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); - } + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + } else { - # otherwise back to last level stored at CASE keyword - $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; - } - # We are in code block + # otherwise back to last level stored at CASE keyword + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + } } + # We reach the last end of the code + elsif ($self->{ '_is_in_block' } > -1 and $self->_next_token eq ';' and !$self->{ '_is_in_exception' }) + { + @{ $self->{ '_level_stack' } } = (); + $self->{ '_level' } = 0; + $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); + } + # We are in code block 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) + # 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) { - $self->{ '_is_in_block' }--; - } - # Go back to level stored with IF/LOOP/BEGIN/EXCEPTION block - $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; - $self->_back if ($self->_next_token =~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i); - } - $self->_new_line; - $self->_add_token( $token ); - } - - elsif ( $token =~ /^(?:UNION|INTERSECT|EXCEPT)$/i ) + $self->{ '_is_in_block' }--; + } + # Go back to level stored with IF/LOOP/BEGIN/EXCEPTION block + $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0; + $self->_back($token, $last) if ($self->_next_token =~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i); + } + $self->_new_line($token,$last); + $self->_add_token( $token ); + } + + elsif ( $token =~ /^(?:UNION|INTERSECT|EXCEPT)$/i ) { - $self->{ 'no_break' } = 0; - if ($self->{ '_is_in_join' }) + $self->{ 'no_break' } = 0; + 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 + $self->_back($token, $last); + $self->{ '_is_in_join' } = 0; + } + $self->_back($token, $last) unless defined $last and $last eq '('; + $self->_new_line($token,$last); + $self->_add_token( $token ); + $self->_new_line($token,$last) 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_where' }-- if ($self->{ '_is_in_where' }); + $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 and (not defined $last or uc($last) ne 'MATCH') ) { - $self->{ 'no_break' } = 0; - if (!$self->{ '_is_in_join' } and ($last and $last ne ')') ) + $self->{ 'no_break' } = 0; + if (!$self->{ '_is_in_join' } and ($last and $last ne ')') ) { - $self->_back; - } - if ($self->{ '_has_over_in_join' }) + $self->_back($token, $last); + } + if ($self->{ '_has_over_in_join' }) { - $self->{ '_has_over_in_join' } = 0; - $self->_back; - } + $self->{ '_has_over_in_join' } = 0; + $self->_back($token, $last); + } - 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 ); - } - if ( ($token =~ /(?:INNER|OUTER)$/i) && ($last !~ /(?:LEFT|RIGHT|CROSS|NATURAL|FULL)$/i) ) + $self->_new_line($token,$last); + $self->_over($token,$last) if ( $self->{ '_level' } == 0 ); + } + 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 ); - } + $self->_new_line($token,$last); + $self->_over($token,$last) if (!$self->{ '_is_in_join' }); + } + $self->_add_token( $token ); + } - elsif ( $token =~ /^(?:JOIN)$/i ) + elsif ( $token =~ /^(?:JOIN)$/i and !$self->{ '_is_in_operator' }) { - $self->{ 'no_break' } = 0; - if ( not defined $last or $last !~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) + $self->{ 'no_break' } = 0; + 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->_add_token( $token ); - $self->{ '_is_in_join' } = 1; - } - - elsif ( $token =~ /^(?:AND|OR)$/i ) + $self->_new_line($token,$last); + $self->_back($token, $last) if ($self->{ '_has_over_in_join' }); + $self->{ '_has_over_in_join' } = 0; + } + $self->_add_token( $token ); + $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+$/) + # Try to detect AND in BETWEEN clause to prevent newline insert + if (uc($token) eq 'AND' and ($self->_next_token() =~ /^\d+$/ || (defined $last && $last =~ /^(PRECEDING|FOLLOWING|ROW)$/i))) { - $self->_add_token( $token ); - next; - } - $self->{ 'no_break' } = 0; - if ($self->{ '_is_in_join' }) + $self->_add_token( $token ); + $last = $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' } + $self->_over($token,$last); + $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 =~ /^--/ ) + $self->_new_line($token,$last); + if (!$self->{'_and_level'} and (!$self->{ '_level' } || $self->{ '_is_in_alter' })) { + $self->_over($token,$last); + } elsif ($self->{'_and_level'} and !$self->{ '_level' } and uc($token) eq 'OR') { + $self->_over($token,$last); + } elsif ($#{$self->{ '_level_stack' }} >= 0 and $self->{ '_level' } == $self->{ '_level_stack' }[-1]) { + $self->_over($token,$last); + } + } + $self->_add_token( $token ); + $self->{'_and_level'}++; + } + + elsif ( $token =~ /^\/\*.*\*\/$/s ) { - if ( !$self->{ 'no_comments' } ) + 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 - { - $self->_new_line; - } - } - - elsif ( $token =~ /^\/\*.*\*\/$/s ) - { - if ( !$self->{ 'no_comments' } ) - { - $token =~ s/\n[\s\t]+\*/\n\*/gs; - $self->_new_line; - $self->_add_token( $token ); - $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); - $self->_new_line; - $self->{ 'break' } = " " unless ( $self->{ 'spaces' } != 0 ); - } - } - - elsif ($token =~ /^USING$/i) + $token =~ s/\n[\s\t]+\*/\n\*/gs; + $self->_new_line($token,$last), $self->_add_token('') if (defined $last and $last eq ';'); + $self->_new_line($token,$last); + $self->_add_token( $token ); + $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); + $self->_new_line($token,$last); + $self->{ 'break' } = " " unless ( $self->{ 'spaces' } != 0 ); + } + } + + elsif ($token =~ /^USING$/i and (!$self->{ '_is_in_policy' } || $self->{ 'format_type' })) { - if (!$self->{ '_is_in_from' }) + $self->{ '_is_in_using' } = 1; + if (!$self->{ '_is_in_from' }) { - $self->_new_line; - } + $self->_over($token,$last) if ($self->{ '_is_in_operator' }); + $self->_new_line($token,$last) if (uc($last) ne 'EXCLUDE'); + } else { - # USING from join clause disable line break - $self->{ 'no_break' } = 1; - $self->{ '_is_in_function' }++; + # USING from join clause disable line break + #$self->{ 'no_break' } = 1; + $self->{ '_is_in_function' }++; + } + $self->_add_token($token); + } + + elsif ($token =~ /^EXCLUDE$/i) + { + if ($last !~ /^(FOLLOWING|ADD)$/i or $self->_next_token !~ /^USING$/i) { + $self->_new_line($token,$last) if ($last !~ /^(FOLLOWING|ADD)$/i); } - $self->_add_token($token); - } - elsif ($token =~ /^\\\S/) + $self->_add_token( $token ); + $self->{ '_is_in_using' } = 1; + } + + elsif ($token =~ /^\\\S/) { - # treat everything starting with a \ and at least one character as psql meta command. - $self->_add_token( $token ); - $self->_new_line; - } + # treat everything starting with a \ and at least one character as psql meta command. + $self->_add_token( $token ); + $self->_new_line($token,$last); + } - elsif ($token =~ /^ADD$/i && ($self->{ '_current_sql_stmt' } eq 'SEQUENCE' + elsif ($token =~ /^ADD|DROP$/i && ($self->{ '_current_sql_stmt' } eq 'SEQUENCE' || $self->{ '_current_sql_stmt' } eq 'ALTER')) { - $self->_new_line; - $self->_over; - $self->_add_token($token); - } + if ($self->_next_token !~ /^NOT|NULL|DEFAULT$/i and (not defined $last or !$self->{ '_is_in_alter' } or $last ne '(')) { + $self->_new_line($token,$last); + if ($self->{ '_is_in_alter' } < 2) { + $self->_over($token,$last); + } + } + $self->_add_token($token, $last); + $self->{ '_is_in_alter' }++ if ($self->{ '_is_in_alter' } == 1); + } - 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); - } + $self->_new_line($token,$last); + $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); - } + $self->_new_line($token,$last); + $self->_add_token($token); + } - elsif (uc($last) ne 'NO' and $token =~ /^(MINVALUE|MAXVALUE)$/i) + elsif ($last !~ /^(\(|NO)$/i and $token =~ /^(MINVALUE|MAXVALUE)$/i) { - $self->_new_line; - $self->_add_token($token); - } + $self->_new_line($token,$last); + $self->_add_token($token); + } - elsif ($token =~ /^CACHE$/i) + elsif ($token =~ /^CACHE$/i) { - $self->_new_line; - $self->_add_token($token); - } + $self->_new_line($token,$last); + $self->_add_token($token); + } - else + else { - if ($last =~ /^(?:SEQUENCE)$/i and $self->_next_token !~ /^OWNED$/i) - { - $self->_add_token( $token ); - $self->_new_line; - $self->_over; + if ($self->{ '_fct_code_delimiter' } and $self->{ '_fct_code_delimiter' } =~ /^'.*'$/) { + $self->{ '_fct_code_delimiter' } = ""; } - else + # special case with comment + if ($token =~ /(?:\s*--)[\ \t\S]*/s) { - 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; - } + $token =~ s/^(\s*)(--.*)/$2/s; + my $start = $1 || ''; + if ($start =~ /\n/s) { + $self->_new_line($token,$last), $self->_add_token('') if (defined $last and $last eq ';' and $self->{ 'content' } !~ /\n$/s); + $self->_new_line($token,$last); } - $self->_add_token( $token, $last ); - if (defined $last && uc($last) eq 'LANGUAGE' && (!defined $self->_next_token || $self->_next_token ne ';')) - { - $self->_new_line; + $token =~ s/\s+$//s; + $token =~ s/^\s+//s; + $self->_add_token( $token ); + $self->_new_line($token,$last) if ($start || $self->{ 'content' } !~ /\n/s); + # Add extra newline after the last comment if we are not in a block or a statement + if (defined $self->_next_token and $self->_next_token !~ /^\s*--/) { + $self->{ 'content' } .= "\n" if ($self->{ '_is_in_block' } == -1 + and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' } + and !$self->{ '_current_sql_stmt' } + and defined $last and $self->_is_comment($last) + ); } - } - } + $last = $token; + next; + } - $last = $token; - $pos++; + if ($last =~ /^(?:SEQUENCE)$/i and $self->_next_token !~ /^(OWNED|;)$/i) + { + $self->_add_token( $token ); + $self->_new_line($token,$last); + $self->_over($token,$last); + } + 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($token,$last); + } + } + } + + $last = $token; + $pos++; } - $self->_new_line; + $self->_new_line(); return; } @@ -17637,74 +18300,97 @@ Code lifted from SQL::Beautify sub _add_token { my ( $self, $token, $last_token ) = @_; - if ( $self->{ 'wrap' } ) { - my $wrap; - if ( $self->_is_keyword( $token ) ) { - $wrap = $self->{ 'wrap' }->{ 'keywords' }; - } - elsif ( $self->_is_constant( $token ) ) { - $wrap = $self->{ 'wrap' }->{ 'constants' }; - } + if ($DEBUG) { + my ($package, $filename, $line) = caller; + print STDERR "DEBUG_ADD: line: $line => last=", ($last_token||''), ", token=$token\n"; + } - if ( $wrap ) { - $token = $wrap->[ 0 ] . $token . $wrap->[ 1 ]; - } + if ( $self->{ 'wrap' } ) { + my $wrap; + if ( $self->_is_keyword( $token ) ) { + $wrap = $self->{ 'wrap' }->{ 'keywords' }; + } + elsif ( $self->_is_constant( $token ) ) { + $wrap = $self->{ 'wrap' }->{ 'constants' }; + } + + if ( $wrap ) { + $token = $wrap->[ 0 ] . $token . $wrap->[ 1 ]; + } } my $last_is_dot = defined( $last_token ) && $last_token eq '.'; my $sp = $self->_indent; - if ( !$self->_is_punctuation( $token ) and !$last_is_dot) { - if ( (!defined($last_token) || $last_token ne '(') && $token ne ')' && ($token !~ /^::/) ) { - $self->{ 'content' } .= $sp if ($token ne ')' - && defined($last_token) - && $last_token ne '::' + if ( !$self->_is_punctuation( $token ) and !$last_is_dot) + { + if ( (!defined($last_token) || $last_token ne '(') && $token ne ')' && $token !~ /^::/ ) { + $self->{ 'content' } .= $sp if ($token ne ')' + && defined($last_token) + && $last_token ne '::' + && $last_token ne '[' && ($token ne '(' || !$self->_is_function( $last_token ) || $self->{ '_is_in_type' }) - ); - $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) { - $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; - } + ); + $self->{ 'content' } .= $sp if (!defined($last_token) && $token); + } elsif ( defined $last_token && $last_token eq '(' && $token ne ')' && $token !~ /^::/ && !$self->{'wrap_after'} && $self->{ '_is_in_with' } == 1) { + $self->{ 'content' } .= $sp; + } elsif ( $self->{ '_is_in_create' } == 2 && defined($last_token)) { + $self->{ 'content' } .= $sp if ($last_token ne '::' and !$self->{ '_is_in_partition' } + and !$self->{ '_is_in_policy' } + and !$self->{ '_is_in_trigger' } + and !$self->{ '_is_in_aggregate' } + and ($last_token ne '(' || !$self->{ '_is_in_index' })); + } elsif (defined $last_token and (!$self->{ '_is_in_operator' } or !$self->{ '_is_in_alter' })) { + $self->{ 'content' } .= $sp if ($last_token eq '(' && ($self->{ '_is_in_type' } or ($self->{ '_is_in_operator' } and !$self->_is_type($token)))); + } elsif ($token eq ')' and $self->{ '_is_in_block' } >= 0 && $self->{ '_is_in_create' }) { + $self->{ 'content' } .= $sp; + } + 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 - if ( $self->{ 'uc_keywords' } && $self->_is_keyword( $token ) ) { - $token = lc( $token ) if ( $self->{ 'uc_keywords' } == 1 ); - $token = uc( $token ) if ( $self->{ 'uc_keywords' } == 2 ); - $token = ucfirst( lc( $token ) ) if ( $self->{ 'uc_keywords' } == 3 ); + my $next_token = $self->_next_token || ''; + + # lowercase/uppercase keywords taking care of function with same name + if ($self->_is_keyword( $token, $next_token, $last_token ) && (!$self->_is_function( $token ) || $next_token ne '(')) { + $token = lc( $token ) if ( $self->{ 'uc_keywords' } == 1 ); + $token = uc( $token ) if ( $self->{ 'uc_keywords' } == 2 ); + $token = ucfirst( lc( $token ) ) if ( $self->{ 'uc_keywords' } == 3 ); } + else + { - # lowercase/uppercase functions - if ( $self->{ 'uc_functions' } && ( my $fct = $self->_is_function( $token ) ) ) { - $token =~ s/$fct/\L$fct\E/i if ( $self->{ 'uc_functions' } == 1 ); - $token =~ s/$fct/\U$fct\E/i if ( $self->{ 'uc_functions' } == 2 ); - $fct = ucfirst( lc( $fct ) ); - $token =~ s/$fct/$fct/i if ( $self->{ 'uc_functions' } == 3 ); + # lowercase/uppercase known functions or words followed by an open parenthesis + # if the token is not a keyword, an open parenthesis or a comment + my $fct = $self->_is_function( $token ) || ''; + if (($fct && $next_token eq '(') + || (!$self->_is_keyword( $token ) && !$next_token eq '(' + && $token ne '(' && !$self->_is_comment( $token )) ) { + $token =~ s/$fct/\L$fct\E/i if ( $self->{ 'uc_functions' } == 1 ); + $token =~ s/$fct/\U$fct\E/i if ( $self->{ 'uc_functions' } == 2 ); + $fct = ucfirst( lc( $fct ) ); + $token =~ s/$fct/$fct/i if ( $self->{ 'uc_functions' } == 3 ); + } } # 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, $next_token); } $self->{ 'content' } .= $token; - $self->{ 'content' } =~ s/\(\s+\(/\(\(/gs; # This can't be the beginning of a new line anymore. $self->{ '_new_line' } = 0; @@ -17719,7 +18405,12 @@ Code lifted from SQL::Beautify =cut sub _over { - my ( $self ) = @_; + my ( $self, $token, $last ) = @_; + + if ($DEBUG) { + my ($package, $filename, $line) = caller; + print STDERR "DEBUG_OVER: line: $line => last=$last, token=$token\n"; + } ++$self->{ '_level' }; } @@ -17733,8 +18424,12 @@ Code lifted from SQL::Beautify =cut sub _back { - my ( $self ) = @_; + my ( $self, $token, $last ) = @_; + if ($DEBUG) { + my ($package, $filename, $line) = caller; + print STDERR "DEBUG_BACK: line: $line => last=$last, token=$token\n"; + } --$self->{ '_level' } if ( $self->{ '_level' } > 0 ); } @@ -17751,10 +18446,10 @@ sub _indent { my ( $self ) = @_; if ( $self->{ '_new_line' } ) { - return $self->{ 'space' } x ( $self->{ 'spaces' } * $self->{ '_level' } ); + return $self->{ 'space' } x ( $self->{ 'spaces' } * ( $self->{ '_level' } // 0 ) ); } else { - return $self->{ 'space' }; + return $self->{ 'space' }; } } @@ -17767,7 +18462,12 @@ Code lifted from SQL::Beautify =cut sub _new_line { - my ( $self ) = @_; + my ( $self, $token, $last ) = @_; + + if ($DEBUG and defined $token) { + my ($package, $filename, $line) = caller; + print STDERR "DEBUG_NL: line: $line => last=", ($last||''), ", token=$token\n"; + } $self->{ 'content' } .= $self->{ 'break' } unless ( $self->{ '_new_line' } ); $self->{ '_new_line' } = 1; @@ -17810,11 +18510,36 @@ Code lifted from SQL::Beautify =cut sub _is_keyword { - my ( $self, $token ) = @_; + my ( $self, $token, $next_token, $last_token ) = @_; + + # Fix some false positive + if (defined $next_token) { + return 0 if (uc($token) eq 'EVENT' and uc($next_token) ne 'TRIGGER'); + } return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'keywords' } }; } +=head2 _is_type + +Check if a token is a known SQL type + +=cut + +sub _is_type { + my ( $self, $token ) = @_; + + return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'types' } }; +} + + +sub _is_sql_keyword { + my ( $self, $token ) = @_; + + return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'sql_keywords' } }; +} + + =head2 _is_comment Check if a token is a SQL or C style comment @@ -17834,16 +18559,18 @@ sub _is_comment { Check if a token is a known SQL function. -Code lifted from SQL::Beautify +Code lifted from SQL::Beautify and rewritten to check one long regexp instead of a lot of small ones. =cut sub _is_function { my ( $self, $token ) = @_; - my @ret = grep( $token =~ /\b[\.]*$_$/i, @{ $self->{ 'functions' } } ); - - return $ret[ 0 ]; + if ( $token =~ $self->{ 'functions_re' } ) { + return $1; + } else { + return undef; + } } =head2 add_keywords @@ -17858,10 +18585,41 @@ sub add_keywords { my $self = shift; for my $keyword ( @_ ) { - push @{ $self->{ 'keywords' } }, ref( $keyword ) ? @{ $keyword } : $keyword; + push @{ $self->{ 'keywords' } }, ref( $keyword ) ? @{ $keyword } : $keyword; } } +=head2 _re_from_list + +Create compiled regexp from prefix, suffix and and a list of values to match. + +=cut + +sub _re_from_list { + my $prefix = shift; + my $suffix = shift; + my (@joined_list, $ret_re); + + for my $list_item ( @_ ) { + push @joined_list, ref( $list_item ) ? @{ $list_item } : $list_item; + } + + $ret_re = "$prefix(" . join('|', @joined_list) . ")$suffix"; + + return qr/$ret_re/i; +} + +=head2 _refresh_functions_re + +Refresh compiled regexp for functions. + +=cut + +sub _refresh_functions_re { + my $self = shift; + $self->{ 'functions_re' } = _re_from_list( '\b[\.]*', '$', @{ $self->{ 'functions' } }); +} + =head2 add_functions Add new functions to highlight. @@ -17874,8 +18632,10 @@ sub add_functions { my $self = shift; for my $function ( @_ ) { - push @{ $self->{ 'functions' } }, ref( $function ) ? @{ $function } : $function; + push @{ $self->{ 'functions' } }, ref( $function ) ? @{ $function } : $function; } + + $self->_refresh_functions_re(); } =head2 add_rule @@ -17909,7 +18669,7 @@ sub _get_rule { values %{ $self->{ 'rules' } }; # Reset iterator. while ( my ( $rule, $list ) = each %{ $self->{ 'rules' } } ) { - return $rule if ( grep { uc( $token ) eq uc( $_ ) } @$list ); + return $rule if ( grep { uc( $token ) eq uc( $_ ) } @$list ); } return; @@ -17927,17 +18687,17 @@ sub _process_rule { my ( $self, $rule, $token ) = @_; my $format = { - break => sub { $self->_new_line }, - over => sub { $self->_over }, - back => sub { $self->_back }, - token => sub { $self->_add_token( $token ) }, - push => sub { push @{ $self->{ '_level_stack' } }, $self->{ '_level' } }, - pop => sub { $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0 }, - reset => sub { $self->{ '_level' } = 0; @{ $self->{ '_level_stack' } } = (); }, + break => sub { $self->_new_line() }, + over => sub { $self->_over() }, + back => sub { $self->_back() }, + token => sub { $self->_add_token( $token ) }, + push => sub { push @{ $self->{ '_level_stack' } }, $self->{ '_level' } }, + pop => sub { $self->{ '_level' } = pop( @{ $self->{ '_level_stack' } } ) || 0 }, + reset => sub { $self->{ '_level' } = 0; @{ $self->{ '_level_stack' } } = (); }, }; for ( split /-/, lc $rule ) { - &{ $format->{ $_ } } if ( $format->{ $_ } ); + &{ $format->{ $_ } } if ( $format->{ $_ } ); } } @@ -17968,7 +18728,7 @@ sub _is_punctuation { if ($self->{ 'comma' } eq 'start' and $token eq ',') { return 0; } - return ( $token =~ /^[,;.]$/ ); + return ( $token =~ /^[,;.\[\]]$/ ); } =head2 _generate_anonymized_string @@ -17990,33 +18750,33 @@ sub _generate_anonymized_string { # Prevent dates format like DD/MM/YYYY HH24:MI:SS from being anonymized return $original if $original =~ m{ - \A - (?:FM|FX|TM)? - (?: - HH | HH12 | HH24 - | MI - | SS - | MS - | US - | SSSS - | AM | A\.M\. | am | a\.m\. - | PM | P\.M\. | pm | p\.m\. - | Y,YYY | YYYY | YYY | YY | Y - | IYYY | IYY | IY | I - | BC | B\.C\. | bc | b\.c\. - | AD | A\.D\. | ad | a\.d\. - | MONTH | Month | month | MON | Mon | mon | MM - | DAY | Day | day | DY | Dy | dy | DDD | DD | D - | W | WW | IW - | CC - | J - | Q - | RM | rm - | TZ | tz - | [\s/:-] - )+ - (?:TH|th|SP)? - \z + \A + (?:FM|FX|TM)? + (?: + HH | HH12 | HH24 + | MI + | SS + | MS + | US + | SSSS + | AM | A\.M\. | am | a\.m\. + | PM | P\.M\. | pm | p\.m\. + | Y,YYY | YYYY | YYY | YY | Y + | IYYY | IYY | IY | I + | BC | B\.C\. | bc | b\.c\. + | AD | A\.D\. | ad | a\.d\. + | MONTH | Month | month | MON | Mon | mon | MM + | DAY | Day | day | DY | Dy | dy | DDD | DD | D + | W | WW | IW + | CC + | J + | Q + | RM | rm + | TZ | tz + | [\s/:-] + )+ + (?:TH|th|SP)? + \z }; # Prevent interval from being anonymized @@ -18032,8 +18792,8 @@ sub _generate_anonymized_string { unless ( $cache->{ $original } ) { - # Actual anonymized version generation - $cache->{ $original } = join( '', map { $chars[ rand @chars ] } 1 .. 10 ); + # Actual anonymized version generation + $cache->{ $original } = join( '', map { $chars[ rand @chars ] } 1 .. 10 ); } return $cache->{ $original }; @@ -18064,9 +18824,9 @@ sub anonymize { # Anonymize each values $query =~ s{ - ([^\s\']+[\s\(]*) # before - '([^']*)' # original - ([\)]*::\w+)? # after + ([^\s\']+[\s\(]*) # before + '([^']*)' # original + ([\)]*::\w+)? # after }{$1 . "'" . $self->_generate_anonymized_string($1, $2, $3) . "'" . ($3||'')}xeg; $query =~ s/\$EMPTYSTRING\$/''/gs; @@ -18104,6 +18864,12 @@ Currently defined defaults: =item colorize => 1 +=item format_type => 0 + +=item wrap_limit => 0 + +=item wrap_after => 0 + =back =cut @@ -18113,22 +18879,28 @@ sub set_defaults { $self->set_dicts(); # Set some defaults. - $self->{ 'query' } = ''; + $self->{ 'query' } = ''; $self->{ 'spaces' } = 4; - $self->{ 'space' } = ' '; - $self->{ 'break' } = "\n"; - $self->{ 'wrap' } = {}; - $self->{ 'rules' } = {}; + $self->{ 'space' } = ' '; + $self->{ 'break' } = "\n"; + $self->{ 'wrap' } = {}; + $self->{ 'rules' } = {}; $self->{ 'uc_keywords' } = 0; $self->{ 'uc_functions' } = 0; $self->{ 'no_comments' } = 0; $self->{ 'placeholder' } = ''; $self->{ 'keywords' } = $self->{ 'dict' }->{ 'pg_keywords' }; - $self->{ 'functions' } = $self->{ 'dict' }->{ 'pg_functions' }; + $self->{ 'types' } = $self->{ 'dict' }->{ 'pg_types' }; + $self->{ 'functions' } = (); + push(@{ $self->{ 'functions' } }, keys %{ $self->{ 'dict' }->{ 'pg_functions' } }); + $self->_refresh_functions_re(); $self->{ 'separator' } = ''; - $self->{ 'comma' } = 'end'; + $self->{ 'comma' } = 'end'; $self->{ 'format' } = 'text'; $self->{ 'colorize' } = 1; + $self->{ 'format_type' } = 0; + $self->{ 'wrap_limit' } = 0; + $self->{ 'wrap_after' } = 0; return; } @@ -18146,7 +18918,7 @@ sub format { my $format = shift; if ( grep(/^$format$/i, 'text', 'html') ) { - $self->{ 'format' } = lc($format); + $self->{ 'format' } = lc($format); return 1; } return 0; @@ -18167,382 +18939,419 @@ sub set_dicts { # First load it all as "my" variables, to make it simpler to modify/map/grep/add # Afterwards, when everything is ready, put it in $self->{'dict'}->{...} - my @pg_keywords = map { uc } qw( - ADD AFTER ALL ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC ASYMMETRIC AUTHORIZATION AUTO_INCREMENT - BACKWARD BEFORE BEGIN BERNOULLI BETWEEN BINARY BOTH BY BY CACHE CASCADE CASE CAST CHECK CHECKPOINT - CLOSE CLUSTER COLLATE COLLATION COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONFLICT CONSTRAINT - CONSTRAINT CONTINUE COPY COST CREATE CROSS CUBE CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP - CURRENT_USER CURSOR CYCLE DATABASE DEALLOCATE DECLARE DEFAULT DEFERRABLE DEFERRED DEFINER DELETE DELIMITER - DESC DISTINCT DO DOMAIN DROP EACH ELSE ENCODING END EXCEPT EXCLUDING EXECUTE EXISTS EXPLAIN EXTENSION FALSE - FETCH FIRST FOR FOREIGN FORWARD FREEZE FROM FULL FUNCTION GRANT GROUP GROUPING HAVING IF ILIKE IMMUTABLE IN - INCLUDING INCREMENT INDEX INHERITS INITIALLY INNER INOUT INSERT INSTEAD INTERSECT INTO INVOKER IS ISNULL - ISOLATION JOIN KEY LANGUAGE LAST LATERAL LC_COLLATE LC_CTYPE LEADING LEAKPROOF LEFT LIKE LIMIT LISTEN LOAD - LOCALTIME LOCALTIMESTAMP LOCATION LOCK LOCKED LOGGED LOGIN LOOP MAPPING MAXVALUE MINVALUE MOVE NATURAL NEXT - NO NOCREATEDB NOCREATEROLE NOSUPERUSER NOT NOTIFY NOTNULL NOWAIT NULL OIDS ON ONLY OPEN OPERATOR OR ORDER - OUTER OVER OVERLAPS OWNER PARTITION PASSWORD PERFORM PLACING POLICY PRECEDING PREPARE PRIMARY PROCEDURE - REASSIGN RECURSIVE REFERENCES REINDEX RENAME REPEATABLE REPLACE REPLICA RESET RESTART RETURN RETURNING - RETURNS RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROWS RULE SAVEPOINT SCHEMA SCROLL SECURITY SELECT SEQUENCE - SEQUENCE SERIALIZABLE SERVER SESSION_USER SET SETOF SETS SHOW SIMILAR SKIP SNAPSHOT SOME STABLE START STRICT - SYMMETRIC SYSTEM TABLE TABLESAMPLE TABLESPACE TEMPLATE TEMPORARY THEN TO TRAILING TRANSACTION TRIGGER TRUE - TRUNCATE TYPE UNBOUNDED UNCOMMITTED UNION UNIQUE UNLISTEN UNLOCK UNLOGGED UPDATE USER USING VACUUM VALUES - VARIADIC VERBOSE VIEW VOLATILE WHEN WHERE WINDOW WITH XOR ZEROFILL + my @pg_keywords = map { uc } qw( + ADD AFTER AGGREGATE ALL ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC ASYMMETRIC AUTHORIZATION ATTACH AUTO_INCREMENT + BACKWARD BEFORE BEGIN BERNOULLI BETWEEN BINARY BOTH BY CACHE CASCADE CASE CAST CHECK CHECKPOINT CLOSE CLUSTER + COLLATE COLLATION COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONFLICT CONSTRAINT CONSTRAINT CONTINUE COPY + COST COSTS CREATE CROSS CUBE CURRENT CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR + CYCLE DATABASE DEALLOCATE DECLARE DEFAULT DEFERRABLE DEFERRED DEFINER DELETE DELIMITER DESC DETACH EVENT DISTINCT + DO DOMAIN DROP EACH ELSE ENCODING END EXCEPT EXCLUDE EXCLUDING EXECUTE EXISTS EXPLAIN EXTENSION FALSE FETCH FILTER + FIRST FOLLOWING FOR FOREIGN FORWARD FREEZE FROM FULL FUNCTION GENERATED GRANT GROUP GROUPING HAVING HASHES HASH + IDENTITY IF ILIKE IMMUTABLE IN INCLUDING INCREMENT INDEX INHERITS INITIALLY INNER INOUT INSERT INSTEAD + INTERSECT INTO INVOKER IS ISNULL ISOLATION JOIN KEY LANGUAGE LAST LATERAL LC_COLLATE LC_CTYPE LEADING + LEAKPROOF LEFT LEFTARG LIKE LIMIT LIST LISTEN LOAD LOCALTIME LOCALTIMESTAMP LOCATION LOCK LOCKED LOGGED LOGIN + LOOP MAPPING MATCH MAXVALUE MERGES MINVALUE MODULUS MOVE NATURAL NEXT ORDINALITY + NO NOCREATEDB NOCREATEROLE NOSUPERUSER NOT NOTIFY NOTNULL NOWAIT NULL OFF OF OIDS ON ONLY OPEN OPERATOR OR ORDER + OUTER OVER OVERLAPS OWNER PARTITION PASSWORD PERFORM PLACING POLICY PRECEDING PREPARE PRIMARY PROCEDURE RANGE + REASSIGN RECURSIVE REFERENCES REINDEX REMAINDER RENAME REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURN RETURNING + RETURNS RETURNS REVOKE RIGHT RIGHTARG ROLE ROLLBACK ROLLUP ROWS ROW RULE SAVEPOINT SCHEMA SCROLL SECURITY SELECT SEQUENCE + SEQUENCE SERIALIZABLE SERVER SESSION_USER SET SETOF SETS SHOW SIMILAR SKIP SNAPSHOT SOME STABLE START STRICT + SYMMETRIC SYSTEM TABLE TABLESAMPLE TABLESPACE TEMPLATE TEMPORARY THEN TO TRAILING TRANSACTION TRIGGER TRUE + TRUNCATE TYPE UNBOUNDED UNCOMMITTED UNION UNIQUE UNLISTEN UNLOCK UNLOGGED UPDATE USER USING VACUUM VALUES + VARIADIC VERBOSE VIEW VOLATILE WHEN WHERE WINDOW WITH WITHIN WORK XOR ZEROFILL + CALL GROUPS INCLUDE OTHERS PROCEDURES ROUTINE ROUTINES TIES READ_ONLY SHAREABLE READ_WRITE + BASETYPE SFUNC STYPE SFUNC1 STYPE1 SSPACE FINALFUNC FINALFUNC_EXTRA FINALFUNC_MODIFY COMBINEFUNC SERIALFUNC DESERIALFUNC + INITCOND MSFUNC MINVFUNC MSTYPE MSSPACE MFINALFUNC MFINALFUNC_EXTRA MFINALFUNC_MODIFY MINITCOND SORTOP + ); + + my @pg_types = qw( + BIGINT BIGSERIAL BIT BOOLEAN BOX BYTEA CHARACTER CIDR CIRCLE DATE DOUBLE INET INT INTEGER INTERVAL JSON + JSONB LINE LSEG MACADDR MACADDR8 MONEY NUMERIC PATH PG_LSN POINT POLYGON REAL SMALLINT SMALLSERIAL + SERIAL TEXT TIME TIMESTAMP TSQUERY TSVECTOR TXID_SNAPSHOT UUID XML INT2 INT4 INT8 VARYING ); my @sql_keywords = map { uc } qw( - ABORT ABSOLUTE ACCESS ACTION ADMIN AGGREGATE ALSO ALWAYS ASSERTION ASSIGNMENT AT ATTRIBUTE BIGINT BOOLEAN - CALLED CASCADED CATALOG CHAIN CHANGE CHARACTER CHARACTERISTICS COLUMNS COMMENTS CONFIGURATION - CONNECTION CONSTRAINTS CONTENT CONVERSION CSV CURRENT DATA DATABASES DAY DEC DECIMAL DEFAULTS DELAYED - DELIMITERS DESCRIBE DICTIONARY DISABLE DISCARD DOCUMENT DOUBLE ENABLE ENCLOSED ENCRYPTED ENUM ESCAPE ESCAPED - EXCLUDE EXCLUSIVE EXTERNAL FIELD FIELDS FLOAT FLUSH FOLLOWING FORCE FUNCTIONS GLOBAL GRANTED GREATEST HANDLER - HEADER HOLD HOUR IDENTIFIED IDENTITY IGNORE IMMEDIATE IMPLICIT INDEXES INFILE INHERIT INLINE INPUT INSENSITIVE - INT INTEGER KEYS KILL LABEL LARGE LEAST LEVEL LINES LOCAL LOW_PRIORITY MATCH MINUTE MODE MODIFY MONTH NAMES - NATIONAL NCHAR NONE NOTHING NULLIF NULLS OBJECT OF OFF OPERATOR OPTIMIZE OPTION OPTIONALLY OPTIONS OUT OUTFILE - OWNED PARSER PARTIAL PASSING PLANS PRECISION PREPARED PRESERVE PRIOR PRIVILEGES PROCEDURAL QUOTE RANGE READ - REAL RECHECK REF REGEXP RELATIVE RELEASE RESTRICT RLIKE ROW SEARCH SECOND SEQUENCES SESSION SHARE SIMPLE - SMALLINT SONAME STANDALONE STATEMENT STATISTICS STATUS STORAGE STRAIGHT_JOIN SYSID TABLES TEMP TERMINATED - TREAT TRUSTED TYPES UNENCRYPTED UNKNOWN UNSIGNED UNTIL USE VALID VALIDATE VALIDATOR VALUE VARIABLES VARYING - WHITESPACE WITHOUT WORK WRAPPER WRITE XMLATTRIBUTES YEAR YES ZONE - ); + ABORT ABSOLUTE ACCESS ACTION ADMIN ALSO ALWAYS ASSERTION ASSIGNMENT AT ATTRIBUTE BIGINT BOOLEAN + CALLED CASCADED CATALOG CHAIN CHANGE CHARACTER CHARACTERISTICS COLUMNS COMMENTS CONFIGURATION + CONNECTION CONSTRAINTS CONTENT CONVERSION CSV CURRENT DATA DATABASES DAY DEC DECIMAL DEFAULTS DELAYED + DELIMITERS DESCRIBE DICTIONARY DISABLE DISCARD DOCUMENT DOUBLE ENABLE ENCLOSED ENCRYPTED ENUM ESCAPE ESCAPED + EXCLUSIVE EXTERNAL FIELD FIELDS FLOAT FLUSH FOLLOWING FORCE FUNCTIONS GLOBAL GRANTED GREATEST HANDLER + HEADER HOLD HOUR IDENTIFIED IGNORE IMMEDIATE IMPLICIT INDEXES INFILE INHERIT INLINE INPUT INSENSITIVE + INT INTEGER KEYS KILL LABEL LARGE LEAST LEVEL LINES LOCAL LOW_PRIORITY MATCH MINUTE MODE MODIFY MONTH NAMES + NATIONAL NCHAR NONE NOTHING NULLIF NULLS OBJECT OFF OPERATOR OPTIMIZE OPTION OPTIONALLY OPTIONS OUT OUTFILE + OWNED PARSER PARTIAL PASSING PLANS PRECISION PREPARED PRESERVE PRIOR PRIVILEGES PROCEDURAL QUOTE READ + REAL RECHECK REF REGEXP RELATIVE RELEASE RLIKE ROW SEARCH SECOND SEQUENCES SESSION SHARE SIMPLE + SMALLINT SONAME STANDALONE STATEMENT STATISTICS STATUS STORAGE STRAIGHT_JOIN SYSID TABLES TEMP TERMINATED + TREAT TRUSTED TYPES UNENCRYPTED UNKNOWN UNSIGNED UNTIL USE VALID VALIDATE VALIDATOR VALUE VARIABLES VARYING + WHITESPACE WITHOUT WORK WRAPPER WRITE XMLATTRIBUTES YEAR YES ZONE + ); my @redshift_keywords = map { uc } qw( - AES128 AES256 ALLOWOVERWRITE BACKUP BLANKSASNULL BYTEDICT BZIP2 CREDENTIALS CURRENT_USER_ID DEFLATE DEFRAG - DELTA DELTA32K DISABLE DISTKEY EMPTYASNULL ENABLE ENCODE ENCRYPT ENCRYPTION EXPLICIT GLOBALDICT256 - GLOBALDICT64K GZIP INTERLEAVED LUN LUNS LZO LZOP MINUS MOSTLY13 MOSTLY32 MOSTLY8 NEW OFFLINE OFFSET OID OLD - PARALLEL PERCENT PERMISSIONS RAW READRATIO RECOVER REJECTLOG RESORT RESPECT RESTORE SORTKEY SYSDATE TAG TDES - TEXT255 TEXT32K TIMESTAMP TOP TRUNCATECOLUMNS WALLET - ); + AES128 AES256 ALLOWOVERWRITE BACKUP BLANKSASNULL BYTEDICT BZIP2 CREDENTIALS CURRENT_USER_ID DEFLATE DEFRAG + DELTA DELTA32K DISABLE DISTKEY EMPTYASNULL ENABLE ENCODE ENCRYPT ENCRYPTION EXPLICIT GLOBALDICT256 + GLOBALDICT64K GZIP INTERLEAVED LUN LUNS LZO LZOP MINUS MOSTLY13 MOSTLY32 MOSTLY8 NEW OFFLINE OFFSET OID OLD + PARALLEL PERCENT PERMISSIONS RAW READRATIO RECOVER REJECTLOG RESORT RESPECT RESTORE SORTKEY SYSDATE TAG TDES + TEXT255 TEXT32K TIMESTAMP TOP TRUNCATECOLUMNS WALLET + ); for my $k ( @pg_keywords ) { - next if grep { $k eq $_ } @sql_keywords; - push @sql_keywords, $k; + next if grep { $k eq $_ } @sql_keywords; + push @sql_keywords, $k; } for my $k ( @redshift_keywords ) { - next if grep { $k eq $_ } @sql_keywords; - push @sql_keywords, $k; + next if grep { $k eq $_ } @sql_keywords; + push @sql_keywords, $k; } my @pg_functions = map { lc } qw( - ascii age bit_length btrim cast char_length character_length coalesce convert chr current_date current_time current_timestamp - count decode date_part date_trunc encode extract get_byte get_bit initcap isfinite interval justify_hours justify_days - lower length lpad ltrim localtime localtimestamp md5 now octet_length overlay position pg_client_encoding - quote_ident quote_literal repeat replace rpad rtrim substring split_part strpos substr set_byte set_bit - trim to_ascii to_hex translate to_char to_date to_timestamp to_number timeofday upper - abbrev abs abstime abstimeeq abstimege abstimegt abstimein abstimele - abstimelt abstimene abstimeout abstimerecv abstimesend aclcontains acldefault - aclexplode aclinsert aclitemeq aclitemin aclitemout aclremove acos - any_in any_out anyarray_in anyarray_out anyarray_recv anyarray_send anyelement_in - anyelement_out anyenum_in anyenum_out anynonarray_in anynonarray_out anyrange_in anyrange_out - anytextcat area areajoinsel areasel armor array_agg array_agg_finalfn - array_agg_transfn array_append array_cat array_dims array_eq array_fill array_ge - array_gt array_in array_larger array_le array_length array_lower array_lt - array_ndims array_ne array_out array_prepend array_recv array_send array_smaller - array_to_json array_to_string array_typanalyze array_upper arraycontained arraycontains arraycontjoinsel - arraycontsel arrayoverlap ascii_to_mic ascii_to_utf8 asin atan atan2 - avg big5_to_euc_tw big5_to_mic big5_to_utf8 bit bit_and bit_in - bit_or bit_out bit_recv bit_send bitand bitcat bitcmp - biteq bitge bitgt bitle bitlt bitne bitnot - bitor bitshiftleft bitshiftright bittypmodin bittypmodout bitxor bool - bool_and bool_or booland_statefunc booleq boolge boolgt boolin - boolle boollt boolne boolor_statefunc boolout boolrecv boolsend - box box_above box_above_eq box_add box_below box_below_eq box_center - box_contain box_contain_pt box_contained box_distance box_div box_eq box_ge - box_gt box_in box_intersect box_le box_left box_lt box_mul - box_out box_overabove box_overbelow box_overlap box_overleft box_overright box_recv - box_right box_same box_send box_sub bpchar bpchar_larger bpchar_pattern_ge - bpchar_pattern_gt bpchar_pattern_le bpchar_pattern_lt bpchar_smaller bpcharcmp bpchareq bpcharge - bpchargt bpchariclike bpcharicnlike bpcharicregexeq bpcharicregexne bpcharin bpcharle - bpcharlike bpcharlt bpcharne bpcharnlike bpcharout bpcharrecv bpcharregexeq - bpcharregexne bpcharsend bpchartypmodin bpchartypmodout broadcast btabstimecmp btarraycmp - btbeginscan btboolcmp btbpchar_pattern_cmp btbuild btbuildempty btbulkdelete btcanreturn - btcharcmp btcostestimate btendscan btfloat48cmp btfloat4cmp btfloat4sortsupport btfloat84cmp - btfloat8cmp btfloat8sortsupport btgetbitmap btgettuple btinsert btint24cmp btint28cmp - btint2cmp btint2sortsupport btint42cmp btint48cmp btint4cmp btint4sortsupport btint82cmp - btint84cmp btint8cmp btint8sortsupport btmarkpos btnamecmp btnamesortsupport btoidcmp - btoidsortsupport btoidvectorcmp btoptions btrecordcmp btreltimecmp btrescan btrestrpos - bttext_pattern_cmp bttextcmp bttidcmp bttintervalcmp btvacuumcleanup bytea_string_agg_finalfn bytea_string_agg_transfn - byteacat byteacmp byteaeq byteage byteagt byteain byteale - bytealike bytealt byteane byteanlike byteaout bytearecv byteasend - cash_cmp cash_div_cash cash_div_flt4 cash_div_flt8 cash_div_int2 cash_div_int4 cash_eq - cash_ge cash_gt cash_in cash_le cash_lt cash_mi cash_mul_flt4 - cash_mul_flt8 cash_mul_int2 cash_mul_int4 cash_ne cash_out cash_pl cash_recv - cash_send cash_words cashlarger cashsmaller cbrt ceil ceiling - center char chareq charge chargt charin charle - charlt charne charout charrecv charsend cideq cidin - cidout cidr cidr_in cidr_out cidr_recv cidr_send cidrecv - cidsend circle circle_above circle_add_pt circle_below circle_center circle_contain - circle_contain_pt circle_contained circle_distance circle_div_pt circle_eq circle_ge circle_gt - circle_in circle_le circle_left circle_lt circle_mul_pt circle_ne circle_out - circle_overabove circle_overbelow circle_overlap circle_overleft circle_overright circle_recv circle_right - circle_same circle_send circle_sub_pt clock_timestamp close_lb close_ls close_lseg - close_pb close_pl close_ps close_sb close_sl col_description concat - concat_ws contjoinsel contsel convert_from convert_to corr cos - cot covar_pop covar_samp crypt cstring_in cstring_out cstring_recv - cstring_send cume_dist current_database current_query current_schema current_schemas current_setting - current_user currtid currtid2 currval date date_cmp date_cmp_timestamp date_cmp_timestamptz date_eq - date_eq_timestamp date_eq_timestamptz date_ge date_ge_timestamp date_ge_timestamptz date_gt date_gt_timestamp - date_gt_timestamptz date_in date_larger date_le date_le_timestamp date_le_timestamptz date_lt - date_lt_timestamp date_lt_timestamptz date_mi date_mi_interval date_mii date_ne date_ne_timestamp - date_ne_timestamptz date_out date_pl_interval date_pli date_recv date_send date_smaller - date_sortsupport daterange daterange_canonical daterange_subdiff datetime_pl datetimetz_pl - dblink_connect_u dblink_connect dblink_disconnect dblink_exec dblink_open dblink_fetch dblink_close - dblink_get_connections dblink_error_message dblink_send_query dblink_is_busy dblink_get_notify dblink_get_result - dblink_cancel_query dblink_get_pkey dblink_build_sql_insert dblink_build_sql_delete dblink_build_sql_update dblink - - dcbrt dearmor decrypt decrypt_iv degrees dense_rank dexp diagonal - diameter digest dispell_init dispell_lexize dist_cpoly dist_lb dist_pb - dist_pc dist_pl dist_ppath dist_ps dist_sb dist_sl div - dlog1 dlog10 domain_in domain_recv dpow dround dsimple_init - dsimple_lexize dsnowball_init dsnowball_lexize dsqrt dsynonym_init dsynonym_lexize dtrunc - elem_contained_by_range encrypt encrypt_iv enum_cmp enum_eq enum_first enum_ge - enum_gt enum_in enum_larger enum_last enum_le enum_lt enum_ne - enum_out enum_range enum_recv enum_send enum_smaller eqjoinsel eqsel - euc_cn_to_mic euc_cn_to_utf8 euc_jis_2004_to_shift_jis_2004 euc_jis_2004_to_utf8 euc_jp_to_mic euc_jp_to_sjis euc_jp_to_utf8 - euc_kr_to_mic euc_kr_to_utf8 euc_tw_to_big5 euc_tw_to_mic euc_tw_to_utf8 every exp - factorial family fdw_handler_in fdw_handler_out first_value float4 float48div - float48eq float48ge float48gt float48le float48lt float48mi float48mul - float48ne float48pl float4_accum float4abs float4div float4eq float4ge - float4gt float4in float4larger float4le float4lt float4mi float4mul - float4ne float4out float4pl float4recv float4send float4smaller float4um - float4up float8 float84div float84eq float84ge float84gt float84le - float84lt float84mi float84mul float84ne float84pl float8_accum float8_avg - float8_corr float8_covar_pop float8_covar_samp float8_regr_accum float8_regr_avgx float8_regr_avgy float8_regr_intercept - float8_regr_r2 float8_regr_slope float8_regr_sxx float8_regr_sxy float8_regr_syy float8_stddev_pop float8_stddev_samp - float8_var_pop float8_var_samp float8abs float8div float8eq float8ge float8gt - float8in float8larger float8le float8lt float8mi float8mul float8ne - float8out float8pl float8recv float8send float8smaller float8um float8up - floor flt4_mul_cash flt8_mul_cash fmgr_c_validator fmgr_internal_validator fmgr_sql_validator format - format_type gb18030_to_utf8 gbk_to_utf8 gen_random_bytes gen_salt generate_series generate_subscripts - get_current_ts_config getdatabaseencoding getpgusername gin_cmp_prefix gin_cmp_tslexeme gin_extract_tsquery gin_extract_tsvector - gin_tsquery_consistent ginarrayconsistent ginarrayextract ginbeginscan ginbuild ginbuildempty ginbulkdelete - gincostestimate ginendscan gingetbitmap gininsert ginmarkpos ginoptions ginqueryarrayextract - ginrescan ginrestrpos ginvacuumcleanup gist_box_compress gist_box_consistent gist_box_decompress gist_box_penalty - gist_box_picksplit gist_box_same gist_box_union gist_circle_compress gist_circle_consistent gist_point_compress gist_point_consistent - gist_point_distance gist_poly_compress gist_poly_consistent gistbeginscan gistbuild gistbuildempty gistbulkdelete - gistcostestimate gistendscan gistgetbitmap gistgettuple gistinsert gistmarkpos gistoptions - gistrescan gistrestrpos gistvacuumcleanup gtsquery_compress gtsquery_consistent gtsquery_decompress gtsquery_penalty - gtsquery_picksplit gtsquery_same gtsquery_union gtsvector_compress gtsvector_consistent gtsvector_decompress gtsvector_penalty - gtsvector_picksplit gtsvector_same gtsvector_union gtsvectorin gtsvectorout has_any_column_privilege has_column_privilege - has_database_privilege has_foreign_data_wrapper_privilege has_function_privilege has_language_privilege has_schema_privilege - has_sequence_privilege has_server_privilege has_table_privilege has_tablespace_privilege has_type_privilege hash_aclitem - hash_array hash_numeric hash_range hashbeginscan hashbpchar hashbuild hashbuildempty hashbulkdelete hashchar hashcostestimate - hashendscan hashenum hashfloat4 hashfloat8 hashgetbitmap hashgettuple hashinet - hashinsert hashint2 hashint2vector hashint4 hashint8 hashmacaddr hashmarkpos - hashname hashoid hashoidvector hashoptions hashrescan hashrestrpos hashtext - hashvacuumcleanup hashvarlena height hmac host hostmask iclikejoinsel - iclikesel icnlikejoinsel icnlikesel icregexeqjoinsel icregexeqsel icregexnejoinsel icregexnesel - inet_client_addr inet_client_port inet_in inet_out inet_recv inet_send inet_server_addr - inet_server_port inetand inetmi inetmi_int8 inetnot inetor inetpl - int2 int24div int24eq int24ge int24gt int24le int24lt - int24mi int24mul int24ne int24pl int28div int28eq int28ge - int28gt int28le int28lt int28mi int28mul int28ne int28pl - int2_accum int2_avg_accum int2_mul_cash int2_sum int2abs int2and int2div - int2eq int2ge int2gt int2in int2larger int2le int2lt - int2mi int2mod int2mul int2ne int2not int2or int2out - int2pl int2recv int2send int2shl int2shr int2smaller int2um - int2up int2vectoreq int2vectorin int2vectorout int2vectorrecv int2vectorsend int2xor - int4 int42div int42eq int42ge int42gt int42le int42lt - int42mi int42mul int42ne int42pl int48div int48eq int48ge - int48gt int48le int48lt int48mi int48mul int48ne int48pl - int4_accum int4_avg_accum int4_mul_cash int4_sum int4abs int4and int4div - int4eq int4ge int4gt int4in int4inc int4larger int4le - int4lt int4mi int4mod int4mul int4ne int4not int4or - int4out int4pl int4range int4range_canonical int4range_subdiff int4recv int4send - int4shl int4shr int4smaller int4um int4up int4xor int8 - int82div int82eq int82ge int82gt int82le int82lt int82mi - int82mul int82ne int82pl int84div int84eq int84ge int84gt - int84le int84lt int84mi int84mul int84ne int84pl int8_accum - int8_avg int8_avg_accum int8_sum int8abs int8and int8div int8eq - int8ge int8gt int8in int8inc int8inc_any int8inc_float8_float8 int8larger - int8le int8lt int8mi int8mod int8mul int8ne int8not - int8or int8out int8pl int8pl_inet int8range int8range_canonical int8range_subdiff - int8recv int8send int8shl int8shr int8smaller int8um int8up - int8xor integer_pl_date inter_lb inter_sb inter_sl internal_in internal_out - interval_accum interval_avg interval_cmp interval_div interval_eq interval_ge interval_gt - interval_hash interval_in interval_larger interval_le interval_lt interval_mi interval_mul - interval_ne interval_out interval_pl interval_pl_date interval_pl_time interval_pl_timestamp interval_pl_timestamptz - interval_pl_timetz interval_recv interval_send interval_smaller interval_transform interval_um intervaltypmodin - intervaltypmodout intinterval isclosed isempty ishorizontal iso8859_1_to_utf8 iso8859_to_utf8 - iso_to_koi8r iso_to_mic iso_to_win1251 iso_to_win866 isopen isparallel isperp - isvertical johab_to_utf8 json_array_elements jsonb_array_elements json_array_elements_text jsonb_array_elements_text - json_array_length jsonb_array_length json_build_array json_build_object json_each jsonb_each json_each_text - jsonb_each_text json_extract_path jsonb_extract_path json_extract_path_text jsonb_extract_path_text json_in json_object - json_object_keys jsonb_object_keys json_out json_populate_record jsonb_populate_record json_populate_recordset jsonb_pretty - jsonb_populate_recordset json_recv json_send jsonb_set json_typeof jsonb_typeof json_to_record jsonb_to_record json_to_recordset - jsonb_to_recordset justify_interval koi8r_to_iso koi8r_to_mic koi8r_to_utf8 koi8r_to_win1251 koi8r_to_win866 koi8u_to_utf8 - lag language_handler_in language_handler_out last_value lastval latin1_to_mic latin2_to_mic latin2_to_win1250 - latin3_to_mic latin4_to_mic lead like_escape likejoinsel - likesel line line_distance line_eq line_horizontal line_in line_interpt - line_intersect line_out line_parallel line_perp line_recv line_send line_vertical - ln lo_close lo_creat lo_create lo_export lo_import lo_lseek - lo_open lo_tell lo_truncate lo_unlink log loread lower_inc - lower_inf lowrite lseg lseg_center lseg_distance lseg_eq lseg_ge - lseg_gt lseg_horizontal lseg_in lseg_interpt lseg_intersect lseg_le lseg_length - lseg_lt lseg_ne lseg_out lseg_parallel lseg_perp lseg_recv lseg_send - lseg_vertical macaddr_and macaddr_cmp macaddr_eq macaddr_ge macaddr_gt macaddr_in - macaddr_le macaddr_lt macaddr_ne macaddr_not macaddr_or macaddr_out macaddr_recv - macaddr_send makeaclitem masklen max mic_to_ascii mic_to_big5 mic_to_euc_cn - mic_to_euc_jp mic_to_euc_kr mic_to_euc_tw mic_to_iso mic_to_koi8r mic_to_latin1 mic_to_latin2 - mic_to_latin3 mic_to_latin4 mic_to_sjis mic_to_win1250 mic_to_win1251 mic_to_win866 min - mktinterval mod money mul_d_interval name nameeq namege - namegt nameiclike nameicnlike nameicregexeq nameicregexne namein namele - namelike namelt namene namenlike nameout namerecv nameregexeq - nameregexne namesend neqjoinsel neqsel netmask network network_cmp - network_eq network_ge network_gt network_le network_lt network_ne network_sub - network_subeq network_sup network_supeq nextval nlikejoinsel nlikesel notlike - npoints nth_value ntile numeric numeric_abs numeric_accum numeric_add - numeric_avg numeric_avg_accum numeric_cmp numeric_div numeric_div_trunc numeric_eq numeric_exp - numeric_fac numeric_ge numeric_gt numeric_in numeric_inc numeric_larger numeric_le - numeric_ln numeric_log numeric_lt numeric_mod numeric_mul numeric_ne numeric_out - numeric_power numeric_recv numeric_send numeric_smaller numeric_sqrt numeric_stddev_pop numeric_stddev_samp - numeric_sub numeric_transform numeric_uminus numeric_uplus numeric_var_pop numeric_var_samp numerictypmodin - numerictypmodout numnode numrange numrange_subdiff obj_description oid oideq - oidge oidgt oidin oidlarger oidle oidlt oidne - oidout oidrecv oidsend oidsmaller oidvectoreq oidvectorge oidvectorgt - oidvectorin oidvectorle oidvectorlt oidvectorne oidvectorout oidvectorrecv oidvectorsend - oidvectortypes on_pb on_pl on_ppath on_ps on_sb on_sl - opaque_in opaque_out overlaps path path_add path_add_pt path_center - path_contain_pt path_distance path_div_pt path_in path_inter path_length path_mul_pt - path_n_eq path_n_ge path_n_gt path_n_le path_n_lt path_npoints path_out - path_recv path_send path_sub_pt pclose percent_rank pg_advisory_lock pg_advisory_lock_shared - pg_advisory_unlock pg_advisory_unlock_all pg_advisory_unlock_shared pg_advisory_xact_lock pg_advisory_xact_lock_shared - pg_available_extension_versions pg_available_extensions pg_backend_pid pg_cancel_backend pg_char_to_encoding pg_collation_for - pg_collation_is_visible pg_column_size pg_conf_load_time pg_conversion_is_visible pg_create_restore_point pg_current_xlog_insert_location - pg_current_xlog_location pg_cursor pg_database_size pg_describe_object pg_encoding_max_length pg_encoding_to_char pg_export_snapshot - pg_extension_config_dump pg_extension_update_paths pg_function_is_visible pg_get_constraintdef pg_get_expr pg_get_function_arguments - pg_get_function_identity_arguments pg_get_function_result pg_get_functiondef pg_get_indexdef pg_get_keywords - pg_get_ruledef pg_get_serial_sequence pg_get_triggerdef pg_get_userbyid pg_get_viewdef pg_has_role pg_indexes_size - pg_is_in_recovery pg_is_other_temp_schema pg_is_xlog_replay_paused pg_last_xact_replay_timestamp pg_last_xlog_receive_location - pg_last_xlog_replay_location pg_listening_channels pg_lock_status pg_ls_dir pg_my_temp_schema pg_node_tree_in pg_node_tree_out - pg_node_tree_recv pg_node_tree_send pg_notify pg_opclass_is_visible pg_operator_is_visible pg_opfamily_is_visible pg_options_to_table - pg_postmaster_start_time pg_prepared_statement pg_prepared_xact pg_read_binary_file pg_read_file pg_relation_filenode pg_relation_filepath - pg_relation_size pg_reload_conf pg_rotate_logfile pg_sequence_parameters pg_show_all_settings pg_size_pretty pg_sleep pg_start_backup - pg_stat_clear_snapshot pg_stat_file pg_stat_get_activity pg_stat_get_analyze_count pg_stat_get_autoanalyze_count pg_stat_get_autovacuum_count - pg_stat_get_backend_activity pg_stat_get_backend_activity_start pg_stat_get_backend_client_addr pg_stat_get_backend_client_port - pg_stat_get_backend_dbid pg_stat_get_backend_idset pg_stat_get_backend_pid pg_stat_get_backend_start pg_stat_get_backend_userid - pg_stat_get_backend_waiting pg_stat_get_backend_xact_start pg_stat_get_bgwriter_buf_written_checkpoints pg_stat_get_bgwriter_buf_written_clean - pg_stat_get_bgwriter_maxwritten_clean pg_stat_get_bgwriter_requested_checkpoints pg_stat_get_bgwriter_stat_reset_time - pg_stat_get_bgwriter_timed_checkpoints pg_stat_get_blocks_fetched pg_stat_get_blocks_hit pg_stat_get_buf_alloc pg_stat_get_buf_fsync_backend - pg_stat_get_buf_written_backend pg_stat_get_checkpoint_sync_time pg_stat_get_checkpoint_write_time pg_stat_get_db_blk_read_time - pg_stat_get_db_blk_write_time pg_stat_get_db_blocks_fetched pg_stat_get_db_blocks_hit pg_stat_get_db_conflict_all pg_stat_get_db_conflict_bufferpin - pg_stat_get_db_conflict_lock pg_stat_get_db_conflict_snapshot pg_stat_get_db_conflict_startup_deadlock pg_stat_get_db_conflict_tablespace - pg_stat_get_db_deadlocks pg_stat_get_db_numbackends pg_stat_get_db_stat_reset_time pg_stat_get_db_temp_bytes pg_stat_get_db_temp_files - pg_stat_get_db_tuples_deleted pg_stat_get_db_tuples_fetched pg_stat_get_db_tuples_inserted pg_stat_get_db_tuples_returned pg_stat_get_db_tuples_updated - pg_stat_get_db_xact_commit pg_stat_get_db_xact_rollback pg_stat_get_dead_tuples pg_stat_get_function_calls pg_stat_get_function_self_time - pg_stat_get_function_total_time pg_stat_get_last_analyze_time pg_stat_get_last_autoanalyze_time pg_stat_get_last_autovacuum_time - pg_stat_get_last_vacuum_time pg_stat_get_live_tuples pg_stat_get_numscans pg_stat_get_tuples_deleted pg_stat_get_tuples_fetched - pg_stat_get_tuples_hot_updated pg_stat_get_tuples_inserted pg_stat_get_tuples_returned pg_stat_get_tuples_updated pg_stat_get_vacuum_count - pg_stat_get_wal_senders pg_stat_get_xact_blocks_fetched pg_stat_get_xact_blocks_hit pg_stat_get_xact_function_calls pg_stat_get_xact_function_self_time - pg_stat_get_xact_function_total_time pg_stat_get_xact_numscans pg_stat_get_xact_tuples_deleted pg_stat_get_xact_tuples_fetched - pg_stat_get_xact_tuples_hot_updated pg_stat_get_xact_tuples_inserted pg_stat_get_xact_tuples_returned pg_stat_get_xact_tuples_updated pg_stat_reset - pg_stat_reset_shared pg_stat_reset_single_function_counters pg_stat_reset_single_table_counters pg_stop_backup pg_switch_xlog pg_table_is_visible - pg_table_size pg_tablespace_databases pg_tablespace_location pg_tablespace_size pg_terminate_backend pg_timezone_abbrevs pg_timezone_names - pg_total_relation_size pg_trigger_depth pg_try_advisory_lock pg_try_advisory_lock_shared pg_try_advisory_xact_lock pg_try_advisory_xact_lock_shared - pg_ts_config_is_visible pg_ts_dict_is_visible pg_ts_parser_is_visible pg_ts_template_is_visible - pg_type_is_visible pg_typeof pg_xact_commit_timestamp pg_last_committed_xact pg_xlog_location_diff pg_xlog_replay_pause pg_xlog_replay_resume pg_xlogfile_name pg_xlogfile_name_offset - pgp_key_id pgp_pub_decrypt pgp_pub_decrypt_bytea pgp_pub_encrypt pgp_pub_encrypt_bytea pgp_sym_decrypt pgp_sym_decrypt_bytea - pgp_sym_encrypt pgp_sym_encrypt_bytea pi plainto_tsquery plpgsql_call_handler plpgsql_inline_handler plpgsql_validator - point point_above point_add point_below point_distance point_div point_eq - point_horiz point_in point_left point_mul point_ne point_out point_recv - point_right point_send point_sub point_vert poly_above poly_below poly_center - poly_contain poly_contain_pt poly_contained poly_distance poly_in poly_left poly_npoints - poly_out poly_overabove poly_overbelow poly_overlap poly_overleft poly_overright poly_recv - poly_right poly_same poly_send polygon popen positionjoinsel positionsel - postgresql_fdw_validator pow power prsd_end prsd_headline prsd_lextype prsd_nexttoken - prsd_start pt_contained_circle pt_contained_poly querytree - quote_nullable radians radius random range_adjacent range_after range_before - range_cmp range_contained_by range_contains range_contains_elem range_eq range_ge range_gist_compress - range_gist_consistent range_gist_decompress range_gist_penalty range_gist_picksplit range_gist_same range_gist_union range_gt - range_in range_intersect range_le range_lt range_minus range_ne range_out - range_overlaps range_overleft range_overright range_recv range_send range_typanalyze range_union - rank record_eq record_ge record_gt record_in record_le record_lt - record_ne record_out record_recv record_send regclass regclassin regclassout - regclassrecv regclasssend regconfigin regconfigout regconfigrecv regconfigsend regdictionaryin - regdictionaryout regdictionaryrecv regdictionarysend regexeqjoinsel regexeqsel regexnejoinsel regexnesel - regexp_matches regexp_replace regexp_split_to_array regexp_split_to_table regoperatorin regoperatorout regoperatorrecv - regoperatorsend regoperin regoperout regoperrecv regopersend regprocedurein regprocedureout - regprocedurerecv regproceduresend regprocin regprocout regprocrecv regprocsend regr_avgx - regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy - regr_syy regtypein regtypeout regtyperecv regtypesend reltime reltimeeq - reltimege reltimegt reltimein reltimele reltimelt reltimene reltimeout - reltimerecv reltimesend reverse round row_number row_to_json - scalargtjoinsel scalargtsel scalarltjoinsel scalarltsel - session_user set_config set_masklen setseed setval setweight shell_in - shell_out shift_jis_2004_to_euc_jis_2004 shift_jis_2004_to_utf8 shobj_description sign similar_escape sin - sjis_to_euc_jp sjis_to_mic sjis_to_utf8 slope smgreq smgrin smgrne - smgrout spg_kd_choose spg_kd_config spg_kd_inner_consistent spg_kd_picksplit spg_quad_choose spg_quad_config - spg_quad_inner_consistent spg_quad_leaf_consistent spg_quad_picksplit spg_text_choose spg_text_config spg_text_inner_consistent spg_text_leaf_consistent - spg_text_picksplit spgbeginscan spgbuild spgbuildempty spgbulkdelete spgcanreturn spgcostestimate - spgendscan spggetbitmap spggettuple spginsert spgmarkpos spgoptions spgrescan - spgrestrpos spgvacuumcleanup sqrt statement_timestamp stddev stddev_pop stddev_samp - string_agg string_agg_finalfn string_agg_transfn string_to_array strip sum - tan text text_ge text_gt text_larger - text_le text_lt text_pattern_ge text_pattern_gt text_pattern_le text_pattern_lt text_smaller - textanycat textcat texteq texticlike texticnlike texticregexeq texticregexne - textin textlen textlike textne textnlike textout textrecv - textregexeq textregexne textsend thesaurus_init thesaurus_lexize tideq tidge - tidgt tidin tidlarger tidle tidlt tidne tidout - tidrecv tidsend tidsmaller time time_cmp time_eq time_ge - time_gt time_hash time_in time_larger time_le time_lt time_mi_interval - time_mi_time time_ne time_out time_pl_interval time_recv time_send time_smaller - time_transform timedate_pl timemi timenow timepl timestamp timestamp_cmp - timestamp_cmp_date timestamp_cmp_timestamptz timestamp_eq timestamp_eq_date timestamp_eq_timestamptz timestamp_ge timestamp_ge_date - timestamp_ge_timestamptz timestamp_gt timestamp_gt_date timestamp_gt_timestamptz timestamp_hash timestamp_in timestamp_larger - timestamp_le timestamp_le_date timestamp_le_timestamptz timestamp_lt timestamp_lt_date timestamp_lt_timestamptz timestamp_mi - timestamp_mi_interval timestamp_ne timestamp_ne_date timestamp_ne_timestamptz timestamp_out timestamp_pl_interval timestamp_recv - timestamp_send timestamp_smaller timestamp_sortsupport timestamp_transform timestamptypmodin timestamptypmodout timestamptz - timestamptz_cmp timestamptz_cmp_date timestamptz_cmp_timestamp timestamptz_eq timestamptz_eq_date timestamptz_eq_timestamp timestamptz_ge - timestamptz_ge_date timestamptz_ge_timestamp timestamptz_gt timestamptz_gt_date timestamptz_gt_timestamp timestamptz_in timestamptz_larger - timestamptz_le timestamptz_le_date timestamptz_le_timestamp timestamptz_lt timestamptz_lt_date timestamptz_lt_timestamp timestamptz_mi - timestamptz_mi_interval timestamptz_ne timestamptz_ne_date timestamptz_ne_timestamp timestamptz_out timestamptz_pl_interval timestamptz_recv - timestamptz_send timestamptz_smaller timestamptztypmodin timestamptztypmodout timetypmodin timetypmodout timetz - timetz_cmp timetz_eq timetz_ge timetz_gt timetz_hash timetz_in timetz_larger - timetz_le timetz_lt timetz_mi_interval timetz_ne timetz_out timetz_pl_interval timetz_recv - timetz_send timetz_smaller timetzdate_pl timetztypmodin timetztypmodout timezone tinterval - tintervalct tintervalend tintervaleq tintervalge tintervalgt tintervalin tintervalle - tintervalleneq tintervallenge tintervallengt tintervallenle tintervallenlt tintervallenne tintervallt - tintervalne tintervalout tintervalov tintervalrecv tintervalrel tintervalsame tintervalsend - tintervalstart to_json to_tsquery to_tsvector transaction_timestamp trigger_out trunc ts_debug - ts_headline ts_lexize ts_match_qv ts_match_tq ts_match_tt ts_match_vq ts_parse - ts_rank ts_rank_cd ts_rewrite ts_stat ts_token_type ts_typanalyze tsmatchjoinsel - tsmatchsel tsq_mcontained tsq_mcontains tsquery_and tsquery_cmp tsquery_eq tsquery_ge - tsquery_gt tsquery_le tsquery_lt tsquery_ne tsquery_not tsquery_or tsqueryin - tsqueryout tsqueryrecv tsquerysend tsrange tsrange_subdiff tstzrange tstzrange_subdiff - tsvector_cmp tsvector_concat tsvector_eq tsvector_ge tsvector_gt tsvector_le tsvector_lt - tsvector_ne tsvectorin tsvectorout tsvectorrecv tsvectorsend txid_current txid_current_snapshot - txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send txid_snapshot_xip txid_snapshot_xmax txid_snapshot_xmin - txid_visible_in_snapshot uhc_to_utf8 unknownin unknownout unknownrecv unknownsend unnest - upper_inc upper_inf utf8_to_ascii utf8_to_big5 utf8_to_euc_cn utf8_to_euc_jis_2004 utf8_to_euc_jp - utf8_to_euc_kr utf8_to_euc_tw utf8_to_gb18030 utf8_to_gbk utf8_to_iso8859 utf8_to_iso8859_1 utf8_to_johab - utf8_to_koi8r utf8_to_koi8u utf8_to_shift_jis_2004 utf8_to_sjis utf8_to_uhc utf8_to_win uuid_cmp - uuid_eq uuid_ge uuid_gt uuid_hash uuid_in uuid_le uuid_lt - uuid_ne uuid_out uuid_recv uuid_send var_pop var_samp varbit - varbit_in varbit_out varbit_recv varbit_send varbit_transform varbitcmp varbiteq - varbitge varbitgt varbitle varbitlt varbitne varbittypmodin varbittypmodout - varchar varchar_transform varcharin varcharout varcharrecv varcharsend varchartypmodin - varchartypmodout variance version void_in void_out void_recv void_send - width width_bucket win1250_to_latin2 win1250_to_mic win1251_to_iso win1251_to_koi8r win1251_to_mic - win1251_to_win866 win866_to_iso win866_to_koi8r win866_to_mic win866_to_win1251 win_to_utf8 xideq - xideqint4 xidin xidout xidrecv xidsend xml xml_in xmlcomment xpath xpath_exists table_to_xmlschema - query_to_xmlschema cursor_to_xmlschema table_to_xml_and_xmlschema query_to_xml_and_xmlschema - schema_to_xml schema_to_xmlschema schema_to_xml_and_xmlschema database_to_xml database_to_xmlschema xmlroot - database_to_xml_and_xmlschema table_to_xml query_to_xmlcursor_to_xml xmlcomment xmlconcat xmlelement xmlforest - xml_is_well_formed_content xml_is_well_formed_document xml_is_well_formed xml_out xml_recv xml_send xmlagg xmlpi - ); + ascii age bit_length btrim cardinality cast char_length character_length coalesce + convert chr current_date current_time current_timestamp count decode date_part date_trunc + encode extract get_byte get_bit initcap isfinite interval justify_hours justify_days + lower length lpad ltrim localtime localtimestamp md5 now octet_length overlay position pg_client_encoding + quote_ident quote_literal repeat replace rpad rtrim substring split_part strpos substr set_byte set_bit + trim to_ascii to_hex translate to_char to_date to_timestamp to_number timeofday upper + abbrev abs abstime abstimeeq abstimege abstimegt abstimein abstimele + abstimelt abstimene abstimeout abstimerecv abstimesend aclcontains acldefault + aclexplode aclinsert aclitemeq aclitemin aclitemout aclremove acos + any_in any_out anyarray_in anyarray_out anyarray_recv anyarray_send anyelement_in + anyelement_out anyenum_in anyenum_out anynonarray_in anynonarray_out anyrange_in anyrange_out + anytextcat area areajoinsel areasel armor array_agg array_agg_finalfn + array_agg_transfn array_append array_cat array_dims array_eq array_fill array_ge array_positions + array_gt array_in array_larger array_le array_length array_lower array_lt array_position + array_ndims array_ne array_out array_prepend array_recv array_remove array_replace array_send array_smaller + array_to_json array_to_string array_typanalyze array_upper arraycontained arraycontains arraycontjoinsel + arraycontsel arrayoverlap ascii_to_mic ascii_to_utf8 asin atan atan2 + avg big5_to_euc_tw big5_to_mic big5_to_utf8 bit bit_and bit_in + bit_or bit_out bit_recv bit_send bitand bitcat bitcmp + biteq bitge bitgt bitle bitlt bitne bitnot + bitor bitshiftleft bitshiftright bittypmodin bittypmodout bitxor bool + bool_and bool_or booland_statefunc booleq boolge boolgt boolin + boolle boollt boolne boolor_statefunc boolout boolrecv boolsend + box box_above box_above_eq box_add box_below box_below_eq box_center + box_contain box_contain_pt box_contained box_distance box_div box_eq box_ge + box_gt box_in box_intersect box_le box_left box_lt box_mul + box_out box_overabove box_overbelow box_overlap box_overleft box_overright box_recv + box_right box_same box_send box_sub bpchar bpchar_larger bpchar_pattern_ge + bpchar_pattern_gt bpchar_pattern_le bpchar_pattern_lt bpchar_smaller bpcharcmp bpchareq bpcharge + bpchargt bpchariclike bpcharicnlike bpcharicregexeq bpcharicregexne bpcharin bpcharle + bpcharlike bpcharlt bpcharne bpcharnlike bpcharout bpcharrecv bpcharregexeq + bpcharregexne bpcharsend bpchartypmodin bpchartypmodout broadcast btabstimecmp btarraycmp + btbeginscan btboolcmp btbpchar_pattern_cmp btbuild btbuildempty btbulkdelete btcanreturn + btcharcmp btcostestimate btendscan btfloat48cmp btfloat4cmp btfloat4sortsupport btfloat84cmp + btfloat8cmp btfloat8sortsupport btgetbitmap btgettuple btinsert btint24cmp btint28cmp + btint2cmp btint2sortsupport btint42cmp btint48cmp btint4cmp btint4sortsupport btint82cmp + btint84cmp btint8cmp btint8sortsupport btmarkpos btnamecmp btnamesortsupport btoidcmp + btoidsortsupport btoidvectorcmp btoptions btrecordcmp btreltimecmp btrescan btrestrpos + bttext_pattern_cmp bttextcmp bttidcmp bttintervalcmp btvacuumcleanup bytea_string_agg_finalfn + bytea_string_agg_transfn byteacat byteacmp byteaeq byteage byteagt byteain byteale + bytealike bytealt byteane byteanlike byteaout bytearecv byteasend + cash_cmp cash_div_cash cash_div_flt4 cash_div_flt8 cash_div_int2 cash_div_int4 cash_eq + cash_ge cash_gt cash_in cash_le cash_lt cash_mi cash_mul_flt4 + cash_mul_flt8 cash_mul_int2 cash_mul_int4 cash_ne cash_out cash_pl cash_recv + cash_send cash_words cashlarger cashsmaller cbrt ceil ceiling + center char chareq charge chargt charin charle + charlt charne charout charrecv charsend cideq cidin + cidout cidr cidr_in cidr_out cidr_recv cidr_send cidrecv + cidsend circle circle_above circle_add_pt circle_below circle_center circle_contain + circle_contain_pt circle_contained circle_distance circle_div_pt circle_eq circle_ge circle_gt + circle_in circle_le circle_left circle_lt circle_mul_pt circle_ne circle_out + circle_overabove circle_overbelow circle_overlap circle_overleft circle_overright circle_recv circle_right + circle_same circle_send circle_sub_pt clock_timestamp close_lb close_ls close_lseg + close_pb close_pl close_ps close_sb close_sl col_description concat + concat_ws contjoinsel contsel convert_from convert_to corr cos + cot covar_pop covar_samp crypt cstring_in cstring_out cstring_recv + cstring_send cume_dist current_database current_query current_schema current_schemas current_setting + current_user currtid currtid2 currval date date_cmp date_cmp_timestamp date_cmp_timestamptz date_eq + date_eq_timestamp date_eq_timestamptz date_ge date_ge_timestamp date_ge_timestamptz date_gt date_gt_timestamp + date_gt_timestamptz date_in date_larger date_le date_le_timestamp date_le_timestamptz date_lt + date_lt_timestamp date_lt_timestamptz date_mi date_mi_interval date_mii date_ne date_ne_timestamp + date_ne_timestamptz date_out date_pl_interval date_pli date_recv date_send date_smaller + date_sortsupport daterange daterange_canonical daterange_subdiff datetime_pl datetimetz_pl + dblink_connect_u dblink_connect dblink_disconnect dblink_exec dblink_open dblink_fetch dblink_close + dblink_get_connections dblink_error_message dblink_send_query dblink_is_busy dblink_get_notify + dblink_get_result dblink_cancel_query dblink_get_pkey dblink_build_sql_insert dblink_build_sql_delete + dblink_build_sql_update dblink dcbrt dearmor decrypt decrypt_iv degrees dense_rank dexp diagonal + decimal diameter digest dispell_init dispell_lexize dist_cpoly dist_lb dist_pb + dist_pc dist_pl dist_ppath dist_ps dist_sb dist_sl div + dlog1 dlog10 domain_in domain_recv dpow dround dsimple_init + dsimple_lexize dsnowball_init dsnowball_lexize dsqrt dsynonym_init dsynonym_lexize dtrunc + elem_contained_by_range encrypt encrypt_iv enum_cmp enum_eq enum_first enum_ge + enum_gt enum_in enum_larger enum_last enum_le enum_lt enum_ne + enum_out enum_range enum_recv enum_send enum_smaller eqjoinsel eqsel + euc_cn_to_mic euc_cn_to_utf8 euc_jis_2004_to_shift_jis_2004 euc_jis_2004_to_utf8 + euc_jp_to_mic euc_jp_to_sjis euc_jp_to_utf8 + euc_kr_to_mic euc_kr_to_utf8 euc_tw_to_big5 euc_tw_to_mic euc_tw_to_utf8 every exp + factorial family fdw_handler_in fdw_handler_out first_value float4 float48div + float48eq float48ge float48gt float48le float48lt float48mi float48mul + float48ne float48pl float4_accum float4abs float4div float4eq float4ge + float4gt float4in float4larger float4le float4lt float4mi float4mul + float4ne float4out float4pl float4recv float4send float4smaller float4um + float4up float8 float84div float84eq float84ge float84gt float84le + float84lt float84mi float84mul float84ne float84pl float8_accum float8_avg + float8_combine float8_regr_combine float8_corr float8_covar_pop float8_covar_samp + float8_regr_accum float8_regr_avgx float8_regr_avgy float8_regr_intercept + float8_regr_r2 float8_regr_slope float8_regr_sxx float8_regr_sxy float8_regr_syy + float8_stddev_pop float8_stddev_samp + float8_var_pop float8_var_samp float8abs float8div float8eq float8ge float8gt + float8in float8larger float8le float8lt float8mi float8mul float8ne + float8out float8pl float8recv float8send float8smaller float8um float8up + floor flt4_mul_cash flt8_mul_cash fmgr_c_validator fmgr_internal_validator fmgr_sql_validator format + format_type gb18030_to_utf8 gbk_to_utf8 gen_random_bytes gen_salt generate_series generate_subscripts + get_current_ts_config getdatabaseencoding getpgusername gin_cmp_prefix gin_cmp_tslexeme gin_extract_tsquery gin_extract_tsvector + gin_tsquery_consistent ginarrayconsistent ginarrayextract ginbeginscan ginbuild ginbuildempty ginbulkdelete + gincostestimate ginendscan gingetbitmap gininsert ginmarkpos ginoptions ginqueryarrayextract + ginrescan ginrestrpos ginvacuumcleanup gist_box_compress gist_box_consistent gist_box_decompress gist_box_penalty + gist_box_picksplit gist_box_same gist_box_union gist_circle_compress gist_circle_consistent gist_point_compress gist_point_consistent + gist_point_distance gist_poly_compress gist_poly_consistent gistbeginscan gistbuild gistbuildempty gistbulkdelete + gistcostestimate gistendscan gistgetbitmap gistgettuple gistinsert gistmarkpos gistoptions + gistrescan gistrestrpos gistvacuumcleanup gtsquery_compress gtsquery_consistent gtsquery_decompress gtsquery_penalty + gtsquery_picksplit gtsquery_same gtsquery_union gtsvector_compress gtsvector_consistent gtsvector_decompress gtsvector_penalty + gtsvector_picksplit gtsvector_same gtsvector_union gtsvectorin gtsvectorout has_any_column_privilege has_column_privilege + has_database_privilege has_foreign_data_wrapper_privilege has_function_privilege has_language_privilege + has_schema_privilege has_sequence_privilege has_server_privilege has_table_privilege has_tablespace_privilege + has_type_privilege hash_aclitem hash_array hash_numeric hash_range hashbeginscan hashbpchar hashbuild + hashbuildempty hashbulkdelete hashchar hashcostestimate hash_aclitem_extended + hashendscan hashenum hashfloat4 hashfloat8 hashgetbitmap hashgettuple hashinet hashinsert hashint2 + hashint2extended hashint2vector hashint4 hashint4extended hashint8 hashint8extended hashmacaddr + hashfloat4extended hashfloat8extended hashcharextended hashoidextended hashnameextended hashmarkpos + hashoidvectorextended hashmacaddrextended hashinetextended hashname hashoid hashoidvector hashoptions + hash_numeric_extended hashmacaddr8extended hash_array_extended hashrescan hashrestrpos hashtext + hashbpcharextended time_hash_extended timetz_hash_extended interval_hash_extended timestamp_hash_extended + uuid_hash_extended pg_lsn_hash_extended hashenumextended jsonb_hash_extended hash_range_extended + hashtextextended hashvacuumcleanup hashvarlena height hmac host hostmask iclikejoinsel + iclikesel icnlikejoinsel icnlikesel icregexeqjoinsel icregexeqsel icregexnejoinsel icregexnesel + inet_client_addr inet_client_port inet_in inet_out inet_recv inet_send inet_server_addr + inet_server_port inetand inetmi inetmi_int8 inetnot inetor inetpl + int int2 int24div int24eq int24ge int24gt int24le int24lt integer + int24mi int24mul int24ne int24pl int28div int28eq int28ge + int28gt int28le int28lt int28mi int28mul int28ne int28pl + int2_accum int2_avg_accum int2_mul_cash int2_sum int2abs int2and int2div + int2eq int2ge int2gt int2in int2larger int2le int2lt + int2mi int2mod int2mul int2ne int2not int2or int2out + int2pl int2recv int2send int2shl int2shr int2smaller int2um + int2up int2vectoreq int2vectorin int2vectorout int2vectorrecv int2vectorsend int2xor + int4 int42div int42eq int42ge int42gt int42le int42lt + int42mi int42mul int42ne int42pl int48div int48eq int48ge + int48gt int48le int48lt int48mi int48mul int48ne int48pl + int4_accum int4_avg_accum int4_mul_cash int4_sum int4abs int4and int4div + int4eq int4ge int4gt int4in int4inc int4larger int4le + int4lt int4mi int4mod int4mul int4ne int4not int4or + int4out int4pl int4range int4range_canonical int4range_subdiff int4recv int4send + int4shl int4shr int4smaller int4um int4up int4xor int8 + int82div int82eq int82ge int82gt int82le int82lt int82mi + int82mul int82ne int82pl int84div int84eq int84ge int84gt + int84le int84lt int84mi int84mul int84ne int84pl int8_accum + int8_avg int8_avg_accum int8_sum int8abs int8and int8div int8eq + int8ge int8gt int8in int8inc int8inc_any int8inc_float8_float8 int8larger + int8le int8lt int8mi int8mod int8mul int8ne int8not + int8or int8out int8pl int8pl_inet int8range int8range_canonical int8range_subdiff + int8recv int8send int8shl int8shr int8smaller int8um int8up + int8xor integer_pl_date inter_lb inter_sb inter_sl internal_in internal_out + interval_accum interval_avg interval_cmp interval_div interval_eq interval_ge interval_gt + interval_hash interval_in interval_larger interval_le interval_lt interval_mi interval_mul + interval_ne interval_out interval_pl interval_pl_date interval_pl_time interval_pl_timestamp interval_pl_timestamptz + interval_pl_timetz interval_recv interval_send interval_smaller interval_transform interval_um intervaltypmodin + intervaltypmodout intinterval isclosed isempty ishorizontal iso8859_1_to_utf8 iso8859_to_utf8 + iso_to_koi8r iso_to_mic iso_to_win1251 iso_to_win866 isopen isparallel isperp + isvertical johab_to_utf8 json_agg jsonb_agg json_array_elements jsonb_array_elements json_array_elements_text jsonb_array_elements_text + json_array_length jsonb_array_length json_build_array json_build_object json_each jsonb_each json_each_text + jsonb_each_text json_extract_path jsonb_extract_path json_extract_path_text jsonb_extract_path_text json_in + json_object json_object_agg jsonb_object_agg json_object_keys jsonb_object_keys json_out json_populate_record + jsonb_populate_record json_populate_recordset jsonb_pretty jsonb_populate_recordset json_recv json_send + jsonb_set json_typeof jsonb_typeof json_to_record jsonb_to_record json_to_recordset jsonb_to_recordset + justify_interval koi8r_to_iso koi8r_to_mic koi8r_to_utf8 koi8r_to_win1251 koi8r_to_win866 koi8u_to_utf8 + jsonb_path_query jsonb_build_object + lag language_handler_in language_handler_out last_value lastval latin1_to_mic latin2_to_mic latin2_to_win1250 + latin3_to_mic latin4_to_mic lead like_escape likejoinsel + likesel line line_distance line_eq line_horizontal line_in line_interpt + line_intersect line_out line_parallel line_perp line_recv line_send line_vertical + ln lo_close lo_creat lo_create lo_export lo_import lo_lseek + lo_open lo_tell lo_truncate lo_unlink log loread lower_inc + lower_inf lowrite lseg lseg_center lseg_distance lseg_eq lseg_ge + lseg_gt lseg_horizontal lseg_in lseg_interpt lseg_intersect lseg_le lseg_length + lseg_lt lseg_ne lseg_out lseg_parallel lseg_perp lseg_recv lseg_send + lseg_vertical macaddr_and macaddr_cmp macaddr_eq macaddr_ge macaddr_gt macaddr_in + macaddr_le macaddr_lt macaddr_ne macaddr_not macaddr_or macaddr_out macaddr_recv + macaddr_send makeaclitem make_interval make_tsrange masklen max mic_to_ascii mic_to_big5 mic_to_euc_cn + mic_to_euc_jp mic_to_euc_kr mic_to_euc_tw mic_to_iso mic_to_koi8r mic_to_latin1 mic_to_latin2 + mic_to_latin3 mic_to_latin4 mic_to_sjis mic_to_win1250 mic_to_win1251 mic_to_win866 min + mktinterval mode mod money mul_d_interval name nameeq namege + namegt nameiclike nameicnlike nameicregexeq nameicregexne namein namele + namelike namelt namene namenlike nameout namerecv nameregexeq + nameregexne namesend neqjoinsel neqsel netmask network network_cmp + network_eq network_ge network_gt network_le network_lt network_ne network_sub + network_subeq network_sup network_supeq nextval nlikejoinsel nlikesel notlike + npoints nth_value ntile numeric numeric_abs numeric_accum numeric_add + numeric_avg numeric_avg_accum numeric_cmp numeric_div numeric_div_trunc numeric_eq numeric_exp + numeric_fac numeric_ge numeric_gt numeric_in numeric_inc numeric_larger numeric_le + numeric_ln numeric_log numeric_lt numeric_mod numeric_mul numeric_ne numeric_out + numeric_power numeric_recv numeric_send numeric_smaller numeric_sqrt numeric_stddev_pop numeric_stddev_samp + numeric_sub numeric_transform numeric_uminus numeric_uplus numeric_var_pop numeric_var_samp numerictypmodin + numerictypmodout numnode numrange numrange_subdiff obj_description oid oideq + oidge oidgt oidin oidlarger oidle oidlt oidne + oidout oidrecv oidsend oidsmaller oidvectoreq oidvectorge oidvectorgt + oidvectorin oidvectorle oidvectorlt oidvectorne oidvectorout oidvectorrecv oidvectorsend + oidvectortypes on_pb on_pl on_ppath on_ps on_sb on_sl + opaque_in opaque_out overlaps path path_add path_add_pt path_center + path_contain_pt path_distance path_div_pt path_in path_inter path_length path_mul_pt + path_n_eq path_n_ge path_n_gt path_n_le path_n_lt path_npoints path_out + path_recv path_send path_sub_pt pclose percent_rank percentile_cont percentile_disc + pg_advisory_lock pg_advisory_lock_shared pg_advisory_unlock pg_advisory_unlock_all pg_advisory_unlock_shared + pg_advisory_xact_lock pg_advisory_xact_lock_shared pg_available_extension_versions pg_available_extensions + pg_backend_pid pg_cancel_backend pg_char_to_encoding pg_collation_for pg_collation_is_visible pg_column_size + pg_conf_load_time pg_conversion_is_visible pg_create_restore_point pg_current_xlog_insert_location + pg_current_xlog_location pg_cursor pg_database_size pg_describe_object pg_encoding_max_length + pg_encoding_to_char pg_export_snapshot pg_extension_config_dump pg_extension_update_paths pg_function_is_visible + pg_get_constraintdef pg_get_expr pg_get_function_arguments pg_filenode_relation pg_indexam_has_property + pg_get_function_identity_arguments pg_get_function_result pg_get_functiondef pg_get_indexdef pg_get_keywords + pg_get_ruledef pg_get_serial_sequence pg_get_triggerdef pg_get_userbyid pg_get_viewdef pg_has_role + pg_indexes_size pg_is_in_recovery pg_is_other_temp_schema pg_is_xlog_replay_paused pg_last_xact_replay_timestamp + pg_last_xlog_receive_location pg_last_xlog_replay_location pg_listening_channels pg_lock_status pg_ls_dir + pg_my_temp_schema pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send pg_notify + pg_opclass_is_visible pg_operator_is_visible pg_opfamily_is_visible pg_options_to_table pg_index_has_property + pg_postmaster_start_time pg_prepared_statement pg_prepared_xact pg_read_binary_file pg_read_file + pg_relation_filenode pg_relation_filepath pg_relation_size pg_reload_conf pg_rotate_logfile + pg_sequence_parameters pg_show_all_settings pg_size_pretty pg_sleep pg_start_backup pg_index_column_has_property + pg_stat_clear_snapshot pg_stat_file pg_stat_get_activity pg_stat_get_analyze_count pg_stat_get_autoanalyze_count + pg_stat_get_autovacuum_count pg_get_object_address pg_identify_object_as_address pg_stat_get_backend_activity + pg_stat_get_backend_activity_start pg_stat_get_backend_client_addr pg_stat_get_backend_client_port + pg_stat_get_backend_dbid pg_stat_get_backend_idset pg_stat_get_backend_pid pg_stat_get_backend_start + pg_stat_get_backend_userid pg_stat_get_backend_waiting pg_stat_get_backend_xact_start + pg_stat_get_bgwriter_buf_written_checkpoints pg_stat_get_bgwriter_buf_written_clean + pg_stat_get_bgwriter_maxwritten_clean pg_stat_get_bgwriter_requested_checkpoints + pg_stat_get_bgwriter_stat_reset_time pg_stat_get_bgwriter_timed_checkpoints pg_stat_get_blocks_fetched + pg_stat_get_blocks_hit pg_stat_get_buf_alloc pg_stat_get_buf_fsync_backend pg_stat_get_buf_written_backend + pg_stat_get_checkpoint_sync_time pg_stat_get_checkpoint_write_time pg_stat_get_db_blk_read_time + pg_stat_get_db_blk_write_time pg_stat_get_db_blocks_fetched pg_stat_get_db_blocks_hit + pg_stat_get_db_conflict_all pg_stat_get_db_conflict_bufferpin pg_stat_get_db_conflict_lock + pg_stat_get_db_conflict_snapshot pg_stat_get_db_conflict_startup_deadlock pg_stat_get_db_conflict_tablespace + pg_stat_get_db_deadlocks pg_stat_get_db_numbackends pg_stat_get_db_stat_reset_time pg_stat_get_db_temp_bytes + pg_stat_get_db_temp_files pg_stat_get_db_tuples_deleted pg_stat_get_db_tuples_fetched + pg_stat_get_db_tuples_inserted pg_stat_get_db_tuples_returned pg_stat_get_db_tuples_updated + pg_stat_get_db_xact_commit pg_stat_get_db_xact_rollback pg_stat_get_dead_tuples pg_stat_get_function_calls + pg_stat_get_function_self_time pg_stat_get_function_total_time pg_stat_get_last_analyze_time + pg_stat_get_last_autoanalyze_time pg_stat_get_last_autovacuum_time pg_stat_get_last_vacuum_time + pg_stat_get_live_tuples pg_stat_get_numscans pg_stat_get_tuples_deleted pg_stat_get_tuples_fetched + pg_stat_get_tuples_hot_updated pg_stat_get_tuples_inserted pg_stat_get_tuples_returned + pg_stat_get_tuples_updated pg_stat_get_vacuum_count pg_stat_get_wal_senders pg_stat_get_xact_blocks_fetched + pg_stat_get_xact_blocks_hit pg_stat_get_xact_function_calls pg_stat_get_xact_function_self_time + pg_stat_get_xact_function_total_time pg_stat_get_xact_numscans pg_stat_get_xact_tuples_deleted + pg_stat_get_xact_tuples_fetched pg_stat_get_xact_tuples_hot_updated pg_stat_get_xact_tuples_inserted + pg_stat_get_xact_tuples_returned pg_stat_get_xact_tuples_updated pg_stat_reset pg_stat_reset_shared + pg_stat_reset_single_function_counters pg_stat_reset_single_table_counters pg_stop_backup pg_switch_xlog + pg_table_is_visible pg_table_size pg_tablespace_databases pg_tablespace_location pg_tablespace_size + pg_terminate_backend pg_timezone_abbrevs pg_timezone_names pg_total_relation_size pg_trigger_depth + pg_try_advisory_lock pg_try_advisory_lock_shared pg_try_advisory_xact_lock pg_try_advisory_xact_lock_shared + pg_ts_config_is_visible pg_ts_dict_is_visible pg_ts_parser_is_visible pg_ts_template_is_visible + pg_type_is_visible pg_typeof pg_xact_commit_timestamp pg_last_committed_xact pg_xlog_location_diff + pg_xlog_replay_pause pg_xlog_replay_resume pg_xlogfile_name pg_xlogfile_name_offset pgp_key_id pgp_pub_decrypt + pgp_pub_decrypt_bytea pgp_pub_encrypt pgp_pub_encrypt_bytea pgp_sym_decrypt pgp_sym_decrypt_bytea + pgp_sym_encrypt pgp_sym_encrypt_bytea pi plainto_tsquery plpgsql_call_handler plpgsql_inline_handler + plpgsql_validator point point_above point_add point_below point_distance point_div point_eq + point_horiz point_in point_left point_mul point_ne point_out point_recv + point_right point_send point_sub point_vert poly_above poly_below poly_center + poly_contain poly_contain_pt poly_contained poly_distance poly_in poly_left poly_npoints + poly_out poly_overabove poly_overbelow poly_overlap poly_overleft poly_overright poly_recv + poly_right poly_same poly_send polygon popen positionjoinsel positionsel + postgresql_fdw_validator pow power prsd_end prsd_headline prsd_lextype prsd_nexttoken + prsd_start pt_contained_circle pt_contained_poly querytree + quote_nullable radians radius random range_adjacent range_after range_before + range_cmp range_contained_by range_contains range_contains_elem range_eq range_ge range_gist_compress + range_gist_consistent range_gist_decompress range_gist_penalty range_gist_picksplit range_gist_same range_gist_union range_gt + range_in range_intersect range_le range_lt range_minus range_ne range_out + range_overlaps range_overleft range_overright range_recv range_send range_typanalyze range_union + rank record_eq record_ge record_gt record_in record_le record_lt + record_ne record_out record_recv record_send regclass regclassin regclassout + regclassrecv regclasssend regconfigin regconfigout regconfigrecv regconfigsend regdictionaryin + regdictionaryout regdictionaryrecv regdictionarysend regexeqjoinsel regexeqsel regexnejoinsel regexnesel + regexp_matches regexp_replace regexp_split_to_array regexp_split_to_table regoperatorin regoperatorout regoperatorrecv + regoperatorsend regoperin regoperout regoperrecv regopersend regprocedurein regprocedureout + regprocedurerecv regproceduresend regprocin regprocout regprocrecv regprocsend regr_avgx + regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy + regr_syy regtypein regtypeout regtyperecv regtypesend reltime reltimeeq + reltimege reltimegt reltimein reltimele reltimelt reltimene reltimeout + reltimerecv reltimesend reverse round row_number row_to_json + scalargtjoinsel scalargtsel scalarltjoinsel scalarltsel + session_user set_config set_masklen setseed setval setweight shell_in + shell_out shift_jis_2004_to_euc_jis_2004 shift_jis_2004_to_utf8 shobj_description sign similar_escape sin + sjis_to_euc_jp sjis_to_mic sjis_to_utf8 slope smgreq smgrin smgrne + smgrout spg_kd_choose spg_kd_config spg_kd_inner_consistent spg_kd_picksplit spg_quad_choose spg_quad_config + spg_quad_inner_consistent spg_quad_leaf_consistent spg_quad_picksplit spg_text_choose spg_text_config spg_text_inner_consistent spg_text_leaf_consistent + spg_text_picksplit spgbeginscan spgbuild spgbuildempty spgbulkdelete spgcanreturn spgcostestimate + spgendscan spggetbitmap spggettuple spginsert spgmarkpos spgoptions spgrescan + spgrestrpos spgvacuumcleanup sqrt statement_timestamp stddev stddev_pop stddev_samp + string_agg string_agg_finalfn string_agg_transfn string_to_array strip sum + tan text text_ge text_gt text_larger + text_le text_lt text_pattern_ge text_pattern_gt text_pattern_le text_pattern_lt text_smaller + textanycat textcat texteq texticlike texticnlike texticregexeq texticregexne + textin textlen textlike textne textnlike textout textrecv + textregexeq textregexne textsend thesaurus_init thesaurus_lexize tideq tidge + tidgt tidin tidlarger tidle tidlt tidne tidout + tidrecv tidsend tidsmaller time time_cmp time_eq time_ge + time_gt time_hash time_in time_larger time_le time_lt time_mi_interval + time_mi_time time_ne time_out time_pl_interval time_recv time_send time_smaller + time_transform timedate_pl timemi timenow timepl timestamp timestamp_cmp + timestamp_cmp_date timestamp_cmp_timestamptz timestamp_eq timestamp_eq_date timestamp_eq_timestamptz timestamp_ge timestamp_ge_date + timestamp_ge_timestamptz timestamp_gt timestamp_gt_date timestamp_gt_timestamptz timestamp_hash timestamp_in timestamp_larger + timestamp_le timestamp_le_date timestamp_le_timestamptz timestamp_lt timestamp_lt_date timestamp_lt_timestamptz timestamp_mi + timestamp_mi_interval timestamp_ne timestamp_ne_date timestamp_ne_timestamptz timestamp_out timestamp_pl_interval timestamp_recv + timestamp_send timestamp_smaller timestamp_sortsupport timestamp_transform timestamptypmodin timestamptypmodout timestamptz + timestamptz_cmp timestamptz_cmp_date timestamptz_cmp_timestamp timestamptz_eq timestamptz_eq_date timestamptz_eq_timestamp timestamptz_ge + timestamptz_ge_date timestamptz_ge_timestamp timestamptz_gt timestamptz_gt_date timestamptz_gt_timestamp timestamptz_in timestamptz_larger + timestamptz_le timestamptz_le_date timestamptz_le_timestamp timestamptz_lt timestamptz_lt_date timestamptz_lt_timestamp timestamptz_mi + timestamptz_mi_interval timestamptz_ne timestamptz_ne_date timestamptz_ne_timestamp timestamptz_out timestamptz_pl_interval timestamptz_recv + timestamptz_send timestamptz_smaller timestamptztypmodin timestamptztypmodout timetypmodin timetypmodout timetz + timetz_cmp timetz_eq timetz_ge timetz_gt timetz_hash timetz_in timetz_larger + timetz_le timetz_lt timetz_mi_interval timetz_ne timetz_out timetz_pl_interval timetz_recv + timetz_send timetz_smaller timetzdate_pl timetztypmodin timetztypmodout timezone tinterval + tintervalct tintervalend tintervaleq tintervalge tintervalgt tintervalin tintervalle + tintervalleneq tintervallenge tintervallengt tintervallenle tintervallenlt tintervallenne tintervallt + tintervalne tintervalout tintervalov tintervalrecv tintervalrel tintervalsame tintervalsend + tintervalstart to_json to_tsquery to_tsvector transaction_timestamp trigger_out trunc ts_debug + ts_headline ts_lexize ts_match_qv ts_match_tq ts_match_tt ts_match_vq ts_parse + ts_rank ts_rank_cd ts_rewrite ts_stat ts_token_type ts_typanalyze tsmatchjoinsel + tsmatchsel tsq_mcontained tsq_mcontains tsquery_and tsquery_cmp tsquery_eq tsquery_ge + tsquery_gt tsquery_le tsquery_lt tsquery_ne tsquery_not tsquery_or tsqueryin + tsqueryout tsqueryrecv tsquerysend tsrange tsrange_subdiff tstzrange tstzrange_subdiff + tsvector_cmp tsvector_concat tsvector_eq tsvector_ge tsvector_gt tsvector_le tsvector_lt + tsvector_ne tsvectorin tsvectorout tsvectorrecv tsvectorsend txid_current txid_current_snapshot + txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send txid_snapshot_xip txid_snapshot_xmax txid_snapshot_xmin + txid_visible_in_snapshot uhc_to_utf8 unknownin unknownout unknownrecv unknownsend unnest + upper_inc upper_inf utf8_to_ascii utf8_to_big5 utf8_to_euc_cn utf8_to_euc_jis_2004 utf8_to_euc_jp + utf8_to_euc_kr utf8_to_euc_tw utf8_to_gb18030 utf8_to_gbk utf8_to_iso8859 utf8_to_iso8859_1 utf8_to_johab + utf8_to_koi8r utf8_to_koi8u utf8_to_shift_jis_2004 utf8_to_sjis utf8_to_uhc utf8_to_win uuid_cmp + uuid_eq uuid_ge uuid_gt uuid_hash uuid_in uuid_le uuid_lt + uuid_ne uuid_out uuid_recv uuid_send var_pop var_samp varbit + varbit_in varbit_out varbit_recv varbit_send varbit_transform varbitcmp varbiteq + varbitge varbitgt varbitle varbitlt varbitne varbittypmodin varbittypmodout + varchar varying varchar_transform varcharin varcharout varcharrecv varcharsend varchartypmodin + varchartypmodout variance version void_in void_out void_recv void_send + width width_bucket win1250_to_latin2 win1250_to_mic win1251_to_iso win1251_to_koi8r win1251_to_mic + win1251_to_win866 win866_to_iso win866_to_koi8r win866_to_mic win866_to_win1251 win_to_utf8 xideq + xideqint4 xidin xidout xidrecv xidsend xml xml_in xmlcomment xpath xpath_exists table_to_xmlschema + query_to_xmlschema cursor_to_xmlschema table_to_xml_and_xmlschema query_to_xml_and_xmlschema + schema_to_xml schema_to_xmlschema schema_to_xml_and_xmlschema database_to_xml database_to_xmlschema xmlroot + database_to_xml_and_xmlschema table_to_xml query_to_xmlcursor_to_xml xmlcomment xmlconcat xmlelement xmlforest + xml_is_well_formed_content xml_is_well_formed_document xml_is_well_formed xml_out xml_recv xml_send xmlagg + xmlpi query_to_xml cursor_to_xml xmlserialize + ); my @copy_keywords = ( 'STDIN', 'STDOUT' ); my %symbols = ( - '=' => '=', '<' => '<', '>' => '>', '|' => '|', ',' => ',', '.' => '.', '+' => '+', '-' => '-', - '*' => '*', '/' => '/', '!=' => '!=', '%' => '%', '<=' => '<=', '>=' => '>=', '<>' => '<>' + '=' => '=', '<' => '<', '>' => '>', '|' => '|', ',' => ',', '.' => '.', '+' => '+', '-' => '-', + '*' => '*', '/' => '/', '!=' => '!=', '%' => '%', '<=' => '<=', '>=' => '>=', '<>' => '<>' ); my @brackets = ( '(', ')' ); # All setting and modification of dicts is done, can set them now to $self->{'dict'}->{...} $self->{ 'dict' }->{ 'pg_keywords' } = \@pg_keywords; + $self->{ 'dict' }->{ 'pg_types' } = \@pg_types; $self->{ 'dict' }->{ 'sql_keywords' } = \@sql_keywords; - $self->{ 'dict' }->{ 'pg_functions' } = \@pg_functions; + $self->{ 'dict' }->{ 'pg_functions' } = (); + map { $self->{ 'dict' }->{ 'pg_functions' }{$_} = ''; } @pg_functions; $self->{ 'dict' }->{ 'copy_keywords' } = \@copy_keywords; $self->{ 'dict' }->{ 'symbols' } = \%symbols; $self->{ 'dict' }->{ 'brackets' } = \@brackets; @@ -18567,23 +19376,23 @@ 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; foreach my $sep (@dynsep) { - while ($$str =~ s/(\Q$sep\E.*?\Q$sep\E)/TEXTVALUE$idx/s) { - $self->{dynamic_code}{$idx} = $1; - $idx++; - } + while ($$str =~ s/(\Q$sep\E.*?\Q$sep\E)/TEXTVALUE$idx/s) { + $self->{dynamic_code}{$idx} = $1; + $idx++; + } } - # Replace any COMMENT constant between single quote - while ($$str =~ s/IS\s+('[^;]+);/IS TEXTVALUE$idx;/s) { - $self->{dynamic_code}{$idx} = $1; - $idx++; + # Replace any COMMENT constant between single quote + while ($$str =~ s/IS\s+('(?:.*?)')\s*;/IS TEXTVALUE$idx;/s) { + $self->{dynamic_code}{$idx} = $1; + $idx++; } } @@ -18596,75 +19405,121 @@ that was removed by the _remove_dynamic_code() method. sub _restore_dynamic_code { - my ($self, $str) = @_; + my ($self, $str) = @_; + + $$str =~ s/TEXTVALUE(\d+)/$self->{dynamic_code}{$1}/gs; + +} + +=head2 _quote_operator + +Internal function used to quote operator with multiple character +to be tokenized as a single word. +The original values are restored with function _restore_operator(). + +=cut + +sub _quote_operator +{ + my ($self, $str) = @_; - $$str =~ s/TEXTVALUE(\d+)/$self->{dynamic_code}{$1}/gs; + while ($$str =~ s/((?:CREATE|DROP|ALTER)\s+OPERATOR\s+(?:IF\s+EXISTS)?)\s*((:?[a-z0-9]+\.)?[\+\-\*\/<>=\~\!\@\#\%\^\&\|\`\?]+)\s*/$1 "$2" /is) { + push(@{ $self->{operator} }, $2) if (!grep(/^\Q$2\E$/, @{ $self->{operator} })); + } + my $idx = 0; + while ($$str =~ s/(NEGATOR|COMMUTATOR)\s*=\s*([^,\)\s]+)/\U$1\E$idx/is) { + $self->{uc($1)}{$idx} = "$1 = $2"; + $idx++; + } } -=head2 _remove_comments (OBSOLETE) +=head2 _restore_operator + +Internal function used to restore operator that was removed +by the _quote_operator() method. + +=cut + +sub _restore_operator +{ + my ($self, $str) = @_; + + foreach my $op (@{ $self->{operator} }) + { + $$str =~ s/"$op"/$op/gs; + } + if (exists $self->{COMMUTATOR}) { + $$str =~ s/COMMUTATOR(\d+)/$self->{COMMUTATOR}{$1}/igs; + } + if (exists $self->{NEGATOR}) { + $$str =~ s/NEGATOR(\d+)/$self->{NEGATOR}{$1}/igs; + } +} + +=head2 _remove_comments Internal function used to remove comments in SQL code -to simplify the work of the parser. Comments are restored -with the _restore_comments() method. +to simplify the work of the wrap_lines. Comments must be +restored with the _restore_comments() method. =cut sub _remove_comments { - my ($self, $content) = @_; + my $self = shift; my $idx = 0; - while ($$content =~ s/(\/\*(.*?)\*\/)/PGF_COMMENT${idx}A/s) { - $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; - $idx++; + while ($self->{ 'content' } =~ s/(\/\*(.*?)\*\/)/PGF_COMMENT${idx}A/s) { + $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; + $idx++; } - my @lines = split(/\n/, $$content); + my @lines = split(/\n/, $self->{ 'content' }); for (my $j = 0; $j <= $#lines; $j++) { - $lines[$j] //= ''; - # Extract multiline comments as a single placeholder - my $old_j = $j; - my $cmt = ''; - while ($lines[$j] =~ /^(\s*\-\-.*)$/) { - $cmt .= "$1\n"; - $j++; - } - if ( $j > $old_j ) { - chomp($cmt); - $lines[$old_j] =~ s/^(\s*\-\-.*)$/PGF_COMMENT${idx}A/; - $self->{'comments'}{"PGF_COMMENT${idx}A"} = $cmt; - $idx++; - $j--; - while ($j > $old_j) { - delete $lines[$j]; - $j--; - } - } - if ($lines[$j] =~ s/(\s*\-\-.*)$/PGF_COMMENT${idx}A/) { - $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; - chomp($self->{'comments'}{"PGF_COMMENT${idx}A"}); - $idx++; - } - - # Mysql supports differents kinds of comment's starter - if ( ($lines[$j] =~ s/(\s*COMMENT\s+'.*)$/PGF_COMMENT${idx}A/) || - ($lines[$j] =~ s/(\s*\# .*)$/PGF_COMMENT${idx}A/) ) { - $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; - chomp($self->{'comments'}{"PGF_COMMENT${idx}A"}); - # Normalize start of comment - $self->{'comments'}{"PGF_COMMENT${idx}A"} =~ s/^(\s*)COMMENT/$1\-\- /; - $self->{'comments'}{"PGF_COMMENT${idx}A"} =~ s/^(\s*)\#/$1\-\- /; - $idx++; - } + $lines[$j] //= ''; + # Extract multiline comments as a single placeholder + my $old_j = $j; + my $cmt = ''; + while ($j <= $#lines && $lines[$j] =~ /^(\s*\-\-.*)$/) { + $cmt .= "$1\n"; + $j++; + } + if ( $j > $old_j ) { + chomp($cmt); + $lines[$old_j] =~ s/^(\s*\-\-.*)$/PGF_COMMENT${idx}A/; + $self->{'comments'}{"PGF_COMMENT${idx}A"} = $cmt; + $idx++; + $j--; + while ($j > $old_j) { + delete $lines[$j]; + $j--; + } + } + if ($lines[$j] =~ s/(\s*\-\-.*)$/PGF_COMMENT${idx}A/) { + $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; + chomp($self->{'comments'}{"PGF_COMMENT${idx}A"}); + $idx++; + } + + # Mysql supports differents kinds of comment's starter + if ( ($lines[$j] =~ s/(\s*COMMENT\s+'.*)$/PGF_COMMENT${idx}A/) || + ($lines[$j] =~ s/(\s*\# .*)$/PGF_COMMENT${idx}A/) ) { + $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; + chomp($self->{'comments'}{"PGF_COMMENT${idx}A"}); + # Normalize start of comment + $self->{'comments'}{"PGF_COMMENT${idx}A"} =~ s/^(\s*)COMMENT/$1\-\- /; + $self->{'comments'}{"PGF_COMMENT${idx}A"} =~ s/^(\s*)\#/$1\-\- /; + $idx++; + } } - $$content = join("\n", @lines); + $self->{ 'content' } = join("\n", @lines); # Replace subsequent comment by a single one - while ($$content =~ s/(PGF_COMMENT\d+A\s*PGF_COMMENT\d+A)/PGF_COMMENT${idx}A/s) { - $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; - $idx++; + while ($self->{ 'content' } =~ s/(PGF_COMMENT\d+A\s*PGF_COMMENT\d+A)/PGF_COMMENT${idx}A/s) { + $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; + $idx++; } } @@ -18677,11 +19532,49 @@ that was removed by the _remove_comments() method. sub _restore_comments { - my ($self, $content) = @_; + my $self = shift; - while ($$content =~ s/(PGF_COMMENT\d+A)[\n]*/$self->{'comments'}{$1}\n/s) { delete $self->{'comments'}{$1}; }; + while ($self->{ 'content' } =~ s/(PGF_COMMENT\d+A)[\n]*/$self->{'comments'}{$1}\n/s) { delete $self->{'comments'}{$1}; }; } +=head2 wrap_lines + +Internal function used to wrap line at a certain length. + +=cut + +sub wrap_lines +{ + my $self = shift; + + return if (!$self->{'wrap_limit'} || !$self->{ 'content' }); + + $self->_remove_comments(); + + my @lines = split(/\n/, $self->{ 'content' }); + $self->{ 'content' } = ''; + + foreach my $l (@lines) + { + # Remove and store the indentation of the line + my $indent = ''; + if ($l =~ s/^(\s+)//) { + $indent = $1; + } + if (length($l) > $self->{'wrap_limit'} + ($self->{'wrap_limit'}*10/100)) + { + $Text::Wrap::columns = $self->{'wrap_limit'}; + my $t = wrap($indent, " "x$self->{ 'spaces' } . $indent, $l); + $self->{ 'content' } .= "$t\n"; + } else { + $self->{ 'content' } .= $indent . "$l\n"; + } + } + + $self->_restore_comments() if ($self->{ 'content' }); + + return; +} =head1 AUTHOR