# postgresql-lib.pl # Common PostgreSQL functions do '../web-lib.pl'; &init_config(); if ($config{'plib'}) { $ENV{$gconfig{'ld_env'}} .= ':' if ($ENV{$gconfig{'ld_env'}}); $ENV{$gconfig{'ld_env'}} .= $config{'plib'}; } if ($config{'psql'} =~ /^(.*)\/bin\/psql$/ && $1 ne '' && $1 ne '/usr') { $ENV{$gconfig{'ld_env'}} .= ':' if ($ENV{$gconfig{'ld_env'}}); $ENV{$gconfig{'ld_env'}} .= "$1/lib"; } %access = &get_module_acl(); if (!$config{'nodbi'}) { # Check if we have DBD::Pg eval <install_driver("Pg"); EOF } # is_postgresql_running() # Returns 1 if yes, 0 if no, -1 if the login is invalid, -2 if there # is a library problem sub is_postgresql_running { local $temp = &tempname(); local $host = $config{'host'} ? "-h $config{'host'}" : ""; local $cmd; if ($config{'login'}) { open(TEMP, ">$temp"); print TEMP "$config{'login'}\n$config{'pass'}\n"; close(TEMP); local $out; $cmd = "$config{'psql'} -u -c '' $host $config{'basedb'} <$temp"; } else { $cmd = "$config{'psql'} -c '' $host $config{'basedb'}"; } if ($config{'unix'} && getpwnam($config{'unix'})) { $cmd = "su $config{'unix'} -c \"$cmd\""; } open(OUT, "$cmd 2>&1 |"); while() { $out .= $_; } close(OUT); unlink($temp); if ($out =~ /setuserid:/i || $out =~ /no\s+password\s+supplied/i || $out =~ /no\s+postgres\s+username/i || $out =~ /authentication\s+failed/i || $out =~ /password:.*password:/i || $out =~ /database.*does.*not/i) { return -1; } elsif ($out =~ /connect.*failed/i || $out =~ /could not connect to server:/) { return 0; } elsif ($out =~ /lib\S+\.so/i) { return -2; } else { return 1; } } # get_postgresql_version() sub get_postgresql_version { local $v = &execute_sql($config{'basedb'}, 'select version()'); $v = $v->{'data'}->[0]->[0]; if ($v =~ /postgresql\s+([0-9\.]+)/i) { return $1; } else { return undef; } } # list_databases() # Returns a list of all databases sub list_databases { local $t = &execute_sql($config{'basedb'}, 'select * from pg_database order by datname'); return map { $_->[0] } @{$t->{'data'}}; } # list_tables(database) # Returns a list of tables in some database sub list_tables { #local $t = &execute_sql($_[0], 'select relname from pg_class where relkind = \'r\' and relname not like \'pg_%\' and relhasrules = \'f\' order by relname'); local $t = &execute_sql($_[0], 'select tablename from pg_tables where tablename not like \'pg_%\' order by tablename'); return map { $_->[0] } @{$t->{'data'}}; } # list_types() # Returns a list of all available field types sub list_types { local $t = &execute_sql($config{'basedb'}, 'select typname from pg_type where typrelid = 0 and typname !~ \'^_.*\' order by typname'); return map { $_->[0] } @{$t->{'data'}}; } # table_structure(database, table) # Returns a list of hashes detailing the structure of a table sub table_structure { local $t = &execute_sql($_[0], "select a.attnum, a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef FROM pg_class c, pg_attribute a, pg_type t WHERE c.relname = '$_[1]' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid order by attnum"); local (@rv, $r); foreach $r (@{$t->{'data'}}) { local $arr; $arr++ if ($r->[2] =~ s/^_//); local $sz = $r->[4] - 4; if ($sz >= 65536) { $sz = int($sz/65536).",".($sz%65536); } push(@rv, { 'field' => $r->[1], 'arr' => $arr ? 'YES' : 'NO', 'type' => $r->[4] < 0 ? $r->[2] : $r->[2]."($sz)", 'null' => $r->[5] =~ /f|0/ ? 'YES' : 'NO' } ); } return @rv; } # execute_sql(database, sql) sub execute_sql { if ($driver_handle && !$config{'unix'} && $_[1] !~ /^(create|drop)\s+database/ && $_[1] !~ /^\\/) { # Use the DBI interface local $cstr = "dbname=$_[0]"; $cstr .= ";host=$config{'host'}" if ($config{'host'}); local $dbh = $driver_handle->connect($cstr, $config{'login'}, $config{'pass'}); $dbh || &error("DBI connect failed : ",$DBI::errstr); $dbh->{'AutoCommit'} = 0; local $cmd = $dbh->prepare($_[1]); if (!$cmd->execute()) { &error(&text('esql', "".&html_escape($_[1])."", "".&html_escape($dbh->errstr)."")); } local (@data, @row); local @titles = @{$cmd->{'NAME'}}; while(@row = $cmd->fetchrow()) { push(@data, [ @row ]); } $cmd->finish(); $dbh->commit(); $dbh->disconnect(); return { 'titles' => \@titles, 'data' => \@data }; } else { # Call the psql program local $temp = &tempname(); open(TEMP, ">$temp"); print TEMP "$config{'login'}\n$config{'pass'}\n"; close(TEMP); local $host = $config{'host'} ? "-h $config{'host'}" : ""; local $cmd = "$config{'psql'} -u -c \"$_[1]\" $host $_[0] <$temp"; if ($config{'unix'}) { $cmd =~ s/"/\\"/g; $cmd = "su $config{'unix'} -c \"$cmd\""; } open(OUT, "$cmd <$temp 2>&1 |"); local ($line, $rv, @data); do { $line = ; } while($line =~ /^(username|password|user name):/i || $line =~ /(warning|notice):/i || $line !~ /\S/); if ($line =~ /^ERROR:\s+(.*)/ || $line =~ /FATAL.*:\s+(.*)/) { &error(&text('esql', "$_[1]", "$1")); } else { local $dash = ; if ($dash =~ /^\s*\+\-/) { # mysql-style output $line = ; $line =~ s/^[\s\|]+//; $line =~ s/[\s\|]+$//; local @titles = split(/\|/, $line); map { s/^\s+//; s/\s+$// } @titles; $line = ; # skip useless dashes while(1) { $line = ; last if (!$line || $line =~ /^\s*\+/); $line =~ s/^[\s\|]+//; $line =~ s/[\s\|]+$//; local @row = split(/\|/, $line); map { s/^\s+//; s/\s+$// } @row; push(@data, \@row); } $rv = { 'titles' => \@titles, 'data' => \@data }; } elsif ($dash !~ /^-/) { # no output, such as from an insert $rv = undef; } else { # psql-style output local @titles = split(/\|/, $line); map { s/^\s+//; s/\s+$// } @titles; while(1) { $line = ; last if (!$line || $line =~ /^\(\d+\s+\S+\)/); local @row = split(/ \| /, $line); map { s/^\s+//; s/\s+$// } @row; push(@data, \@row); } $rv = { 'titles' => \@titles, 'data' => \@data }; } } close(OUT); unlink($temp); return $rv; } } # execute_sql_logged(database, command) sub execute_sql_logged { &additional_log('sql', $_[0], $_[1]); return &execute_sql(@_); } # run_as_postgres(command) sub run_as_postgres { pipe(OUTr, OUTw); local $pid = fork(); if (!$pid) { untie(*STDIN); untie(*STDOUT); untie(*STDERR); close(STDIN); open(STDOUT, ">&OUTw"); open(STDERR, ">&OUTw"); local @u = getpwnam($config{'user'}); $( = $u[3]; $) = "$u[3] $u[3]"; ($>, $<) = ($u[2], $u[2]); exec(@_); print "Exec failed : $!\n"; exit 1; } close(OUTw); return OUTr; } sub can_edit_db { local $d; return 1 if ($access{'dbs'} eq '*'); foreach $d (split(/\s+/, $access{'dbs'})) { return 1 if ($d eq $_[0]); } return 0; } # get_hba_config() # Parses the postgres host access config file sub get_hba_config { local $lnum = 0; open(HBA, $config{'hba_conf'}); while() { s/\r|\n//g; s/^\s*#.*$//g; if (/^\s*host\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)(\s+(\S+))?/) { push(@rv, { 'type' => 'host', 'index' => scalar(@rv), 'line' => $lnum, 'db' => $1, 'address' => $2, 'netmask' => $3, 'auth' => $4, 'arg' => $6 } ); } elsif (/^\s*local\s+(\S+)\s+(\S+)(\s+(\S+))?/) { push(@rv, { 'type' => 'local', 'index' => scalar(@rv), 'line' => $lnum, 'db' => $1, 'auth' => $2, 'arg' => $4 } ); } $lnum++; } close(HBA); return @rv; } # create_hba(&hba) sub create_hba { local $lref = &read_file_lines($config{'hba_conf'}); push(@$lref, &hba_line($_[0])); &flush_file_lines(); } # delete_hba(&hba) sub delete_hba { local $lref = &read_file_lines($config{'hba_conf'}); splice(@$lref, $_[0]->{'line'}, 1); &flush_file_lines(); } # modify_hba(&hba) sub modify_hba { local $lref = &read_file_lines($config{'hba_conf'}); splice(@$lref, $_[0]->{'line'}, 1, &hba_line($_[0])); &flush_file_lines(); } # swap_hba(&hba1, &hba2) sub swap_hba { local $lref = &read_file_lines($config{'hba_conf'}); local $line0 = $lref->[$_[0]->{'line'}]; local $line1 = $lref->[$_[1]->{'line'}]; $lref->[$_[1]->{'line'}] = $line0; $lref->[$_[0]->{'line'}] = $line1; &flush_file_lines(); } sub hba_line { if ($_[0]->{'type'} eq 'host') { return join(" ", 'host', $_[0]->{'db'}, $_[0]->{'address'}, $_[0]->{'netmask'}, $_[0]->{'auth'}, $_[0]->{'arg'} ? ( $_[0]->{'arg'} ) : () ); } else { return join(" ", 'local', $_[0]->{'db'}, $_[0]->{'auth'}, $_[0]->{'arg'} ? ( $_[0]->{'arg'} ) : () ); } } # split_array(value) sub split_array { if ($_[0] =~ /^\{(.*)\}$/) { local @a = split(/,/, $1); return @a; } else { return ( $_[0] ); } } # join_array(values ..) sub join_array { local $alpha; map { $alpha++ if (!/^-?[0-9\.]+/) } @_; return $alpha ? '{'.join(',', map { "'$_'" } @_).'}' : '{'.join(',', @_).'}'; } 1;