How do I get schemas from Perl's DBI? - perl

I am using Perl DBI. I know that $dbase->tables() will return all the tables in the corresponding database. Likewise, I want to know the schemas available in the database. Is there any function available for that?

What you're looking for is: DBI->table_info()
Call it like this:
my $sth = $dbh->table_info('', '%', '');
my $schemas = $dbh->selectcol_arrayref($sth, {Columns => [2]});
print "Schemas: ", join ', ', #$schemas;

This works.
Create a database:
echo 'create table foo (bar integer primary key, quux varchar(30));' | sqlite3 foobar.sqlite
Perl program to print schema:
use 5.010;
use Data::Dumper qw(Dumper);
use DBIx::Class::Schema::Loader qw();
DBIx::Class::Schema::Loader->naming('current');
DBIx::Class::Schema::Loader->use_namespaces(1);
my $dbi_dsn = 'dbi:SQLite:dbname=foobar.sqlite';
my ($dbi_user, $dbi_pass);
my $schema = DBIx::Class::Schema::Loader->connect(
$dbi_dsn, $dbi_user, $dbi_pass, {'AutoCommit' => 1, 'RaiseError' => 1,}
);
for my $source_name ($schema->sources) {
say "*** Source: $source_name";
my $result_source = $schema->source($source_name);
for my $column_name ($result_source->columns) {
say "Column: $column_name";
say Dumper $result_source->column_info($column_name);
}
}
Output:
*** Source: Foo
Column: bar
$VAR1 = {
'data_type' => 'integer',
'is_auto_increment' => 1,
'is_nullable' => 1
};
Column: quux
$VAR1 = {
'data_type' => 'varchar',
'is_nullable' => 1,
'size' => 30
};

Using ODBC to an Oracle database, I had to use this variation on Uncle Arnie's answer:
my $table_info = $dbh->table_info(undef, '%', undef);
my $schemas = $table_info->fetchall_arrayref([1]);
print "Schemas :\n",
join( "\n", map {$_->[0]} #$schemas ), "\n";
Otherwise, $schemas would be undefined when trying to use selectcol_arrayref($sth, ...).

Related

How to incorporate hash inside hash in perl?

my %book = (
'name' => 'abc',
'author' => 'monk',
'isbn' => '123-890',
'issn' => '#issn',
);
my %chapter = (
'title' => 'xyz',
'page' => '90',
);
How do I incorporate %book inside %chapter through reference so that when I write "$chapter{name}", it should print 'abc'?
You can copy the keys/values of the %book into the %chapter:
#chapter{keys %book} = values %book;
Or something like
%chapter = (%chapter, %book);
Now you can say $chapter{name}, but changes in %book are not reflected in %chapter.
You can include the %book via reference:
$chapter{book} = \%book;
Now you could say $chapter{book}{name}, and changes do get reflected.
To have an interface that allows you to say $chapter{name} and that does reflect changes, some advanced techniques would have to be used (this is fairly trivial with tie magic), but don't go there unless you really have to.
You could write a subroutine to check a list of hashes for a key. This program demonstrates:
use strict;
use warnings;
my %book = (
name => 'abc',
author => 'monk',
isbn => '123-890',
issn => '#issn',
);
my %chapter = (
title => 'xyz',
page => '90',
);
for my $key (qw/ name title bogus / ) {
print '>> ', access_hash($key, \%book, \%chapter), "\n";
}
sub access_hash {
my $key = shift;
for my $hash (#_) {
return $hash->{$key} if exists $hash->{$key};
}
undef;
}
output
Use of uninitialized value in print at E:\Perl\source\ht.pl line 17.
>> abc
>> xyz
>>

Deleting Key from 2D Hash in Perl

My Hash looks like this
%hIDSet = (
'TSASD2' => {
'country' => 'US',
'newid' => 'IMRAN',
'oldid' => 'TSASD4'
}
'TS767' => {
'country' => 'DE',
'newid' => 'B90LKT',
'oldid' => '432553'
},
);
when I do
my $sID = "TSASD2";
delete $hIDSet{$sID};
The output I get is
%hIDSet = (
'TSASD2' => {},
'TS767' => {
'country' => 'DE',
'newid' => 'B90LKT',
'oldid' => '432553'
},
);
My question is why the ID is not deleted completely?
You did delete the key, so you must have recreated it before dumping the hash again as in the following snippet:
my $sID = "TSASD2";
my %hIDSet = ( $sID => {} );
delete $hIDSet{$sID};
print(Dumper(\%hIDSet)); # It's gone
if ($hIDSet{$sID}{foo}) { '...' }
print(Dumper(\%hIDSet)); # You've recreated it.
Keep in mind that
$hIDSet{$sID}{foo}
is short for
$hIDSet{$sID}->{foo}
and that
EXPR->{foo}
means
( EXPR //= {} )->{foo}
so
$hIDSet{$sID}{foo}
means
( $hIDSet{$sID} //= {} )->{foo}
Note that this can assign to $hIDSet{$sID}.
Cannot reproduce.
You are confusing the syntax for Perl hashes, and hashref literals. This signifies a hashref:
use Data::Dumper;
my $hashref = {
foo => 'bar', # ← note comma between items
baz => 'qux',
};
delete $hashref->{foo};
print Dumper $hashref;
# $VAR1 = { baz => "qux" };
On the other hand, hashes are just lists:
use Data::Dumper;
my %hash = ( # note parens
foo => 'bar',
baz => 'qux',
);
delete $hash{foo};
print Dumper \%hash;
# $VAR1 = { baz => "qux" };
The code your provided shouldn't compile because of a missing comma, and would fail to run with use strict; use warnings; because of the hash–hashref mismatch. Clean up the types, and it should work allright.

How to use DBD::CSV to get column names row?

I am using DBD::CSV to show csv data. The code I have is:
#! perl
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect("dbi:CSV:", undef, undef, {
f_dir => ".",
f_ext => ".txt/r",
f_lock => 2,
csv_eol => "\n",
csv_sep_char => "|",
csv_quote_char => '"',
csv_escape_char => '"',
csv_class => "Text::CSV_XS",
csv_null => 1,
csv_tables => {
info => {
file => "countries.txt"
}
},
FetchHashKeyName => "NAME_lc",
}) or die $DBI::errstr;
$dbh->{csv_tables}->{countries} = {
skip_first_row => 0,
col_names => ["a","b","c","d"],
raw_header => 1,
};
my $sth = $dbh->prepare ("select * from countries limit 1");
$sth->execute;
while (my #row = $sth->fetchrow_array) {
print join " ", #row;
print "\n"
}
The countries.txt file is like this:
ISO_COUNTRY|COUNTRY_NAME|REGION_CODE|REGION_NAME
AF|Afghanistan|A|Asia
AX|"Aland Islands"|E|Europe
AL|Albania|E|Europe
But when I ran this script, it returns
AF Afghanistan A Asia
I wanted it to return:
ISO_COUNTRY COUNTRY_NAME REGION_CODE REGION_NAME
Does any one know how to achieve this using DBD::CSV module?
Another question is why the col_names attribute setting didn't take effect?
How to make it return the following?
a b c d
$sth->{NAME}, $sth->{NAME_lc} and $sth->{NAME_uc} return a reference to an array containing the names.
my $sth = $dbh->prepare("select * from countries limit 1");
$sth->execute;
print "$_\n" for #{ $sth->{NAME} };

Use of uninitialized value in concatenation (.) or string in slapd_ munin plugin

I'm trying to implement the slapd_ munin plugin which is written in perl which I'm pretty much clueless about. The full plugin is available here. The error I'm getting is this one:
Use of uninitialized value in concatenation (.) or string at
/etc/munin/plugins/slapd_localhost line 232, <DATA> line 275.
Line 232 is this one:
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
I tried debugging by outputing all the variables/objects as follows:
use Data::Dumper; # top of script
# [...]
print Dumper(%ops);
print "action = [$action]\n";
print "basedn = [$basedn]\n\n";
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
When I run it again here is what I obtain:
[...] # 15 other variables belonging to $ops
$VAR16 = {
'info' => 'The graph shows the number of Waiters',
'search' => 'cn=Waiters',
'desc' => 'The current number of Waiters',
'filter' => '(|(cn=Write)(cn=Read))',
'title' => 'Number of Waiters',
'label2' => {
'read' => 'Read',
'write' => 'Write'
},
'vlabel' => 'Waiters'
};
action = [localhost]
action = [cn=Monitor]
Use of uninitialized value in concatenation (.) or string at /etc/munin/plugins/slapd_localhost line 237, <DATA> line 275.
Since all the variables seem to be set, I really don't understand the error message I'm getting
Q: Can anybody advise on how debugging this script?
You should dump a reference to %ops, as in
print Dumper \%ops;
This will make the debug output clearer. To illustrate, consider the output of
#! /usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %h = (foo => { bar => 1 }, baz => { quux => 3 });
print "No reference:\n",
Dumper(%h),
"\n",
"Reference:\n",
Dumper(\%h);
Notice how you see the structure much more clearly in the latter half:
No reference:
$VAR1 = 'baz';
$VAR2 = {
'quux' => 3
};
$VAR3 = 'foo';
$VAR4 = {
'bar' => 1
};
Reference:
$VAR1 = {
'baz' => {
'quux' => 3
},
'foo' => {
'bar' => 1
}
};
You cut out a critical bit of the output. What's the value of $VAR15? Is it "localhost" or something else?
When you print $searchdn, what is its value?

Perl: do and eval result in different answers?

I have a file with the following statements in it:
{
%{do '/tmp/personcontact.pl'},
%{do '/tmp/address.pl'}
}
Now, the temp files are as follows:
Personcontact.pl :
{
'firstname' => {
'__type' => 'String'
},
'lastname' => {
'__type' => 'String'
}
}
Address.pl:
{
'address' => {
'street' => {
'__type' => 'String'
},
'unit' => {
'__type' => 'String',
},
'suburb' => {
'__type' => 'String'
},
'__type' => 'HASH'
}
}
Now, when I do :
open(SCHEMAFILE, "<", $schema) or return undef;
my $schemafile;
while(my $line = <SCHEMAFILE>) { $schemafile .= $line;}
my $tempref = eval $schemafile;
print Dumper $tempref;
The result is $VAR1 = '1/8'
And when I do :
print Dumper do "/tmp/schemawithinschema.pl";
The result is
$VAR1 = 'firstname';
$VAR2 = {
'__type' => 'String'
};
$VAR3 = 'address';
$VAR4 = {
'suburb' => {
'__type' => 'String'
},
'unit' => {
'__type' => 'String'
},
'street' => {
'__type' => 'String'
},
'__type' => 'ARRAY'
};
$VAR5 = 'lastname';
$VAR6 = {
'__type' => 'String'
};
What's wrong here? Thanks!
Alright, to keep this from perpetuating forever, here is a module-based solution for you:
Foo.pm:
package Foo;
use strict;
use warnings;
BEGIN {
require Exporter;
our #ISA = qw( Exporter );
our #EXPORT_OK = qw( get_person get_address get_all );
our $VERSION = '0.01';
}
my %person = (
firstname => {
__type => 'String',
},
lastname => {
__type => 'String',
},
);
my %address = (
address => {
street => {
__type => 'String',
},
unit => {
__type => 'String',
},
suburb => {
__type => 'String',
},
__type => 'HASH',
},
);
sub get_person
{
return \%person;
}
sub get_address
{
return \%address;
}
sub get_all
{
return( { %person, %address } );
}
1;
__END__
bar.pl:
#!/usr/bin/perl
use Data::Dumper;
use strict;
use warnings;
use lib '.';
use Foo qw( get_person get_address get_all );
my $junk = get_all();
print Dumper $junk;
But really, for the sake of your maintenance programmer (often yourself in 6 months), use JSON or YAML (or the faster YAML::XS), so that the data can be maintained as a simple-ish text file, instead of a series of nested data-disguised-as-code references.
To quote Perl Best Practices (not sure if it was Damian originally):
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
EDIT: For completeness, here is the equivalent solution using YAML (from CPAN):
data.yml:
---
firstname:
__type: String
lastname:
__type: String
address:
__type: HASH
street:
__type: String
suburb:
__type: String
unit:
__type: String
baz.pl:
#!/usr/bin/perl
use YAML qw( Load Dump LoadFile DumpFile );
use Data::Dumper;
use strict;
use warnings;
my $data = LoadFile( 'data.yml' );
print Dumper $data;
One small pointer. That '1/8' is what you get when you evaluate a hash in a scalar context. The 8 is the number of buckets assigned to the hash and the 1 is the number of buckets that are in use.
It's generally useless, other than as a flag that you're doing something wrong.
While the intent of the question makes me cry, the difference between your two code snippets has nothing to do with do or eval and everything to do with context. And since that is a legitimate Perl topic, I'll briefly answer it.
In
my $tempref = eval $schemafile;
, the eval takes place in scalar context (imposed by the assignment to $tempref). $schemafile, however, contains a hash, created by the hash reference dereference operator %{}. When that hash is evaluated as a scalar it produces 1/8, normal behavior for a hash.
In
print Dumper do "/tmp/schemawithinschema.pl";
, the do takes place in the list context imposed by the Dumper call (which in turn is in the list context of the print). do creates the same hash that the eval did, but now it's being evaluated in list context, in fact as a list of arguments to Dumper. The top-level hash gets flattened into a list of Label => HashRef pairs, but that's not enough to stop Dumper from being able to show you something that looks a lot like the hash you were trying to create.
For future reference, it is helpful when trying to pinpoint a strange difference of behavior to present exactly the same call in both cases. The more variables between two test cases, the more things that you weren't expecting to matter will wind up mattering and confuse you.
All of that said, the real answer to "What's wrong here?" remains "Trying to do this at all.".