#
# Copyright (c) 1994,1995,1996,1998 Tim Bunce
# portions Copyright (c) 1997-2004 Jeff Urlwin
# portions Copyright (c) 1997 Thomas K. Wenrich
# portions Copyright (c) 2007-2014 Martin J. Evans
#
# You may distribute under the terms of either the GNU General Public
# License or the Artistic License, as specified in the Perl README file.
## no critic (ProhibitManyArgs ProhibitMultiplePackages)
require 5.008;
# NOTE: Don't forget to update the version reference in the POD below too.
# NOTE: If you create a developer release x.y_z ensure y is greater than
# the preceding y in the non developer release e.g., 1.24 should be followed
# by 1.25_1 and then released as 1.26.
# see discussion on dbi-users at
# http://www.nntp.perl.org/group/perl.dbi.dev/2010/07/msg6096.html and
# http://www.dagolden.com/index.php/369/version-numbers-should-be-boring/
$DBD::ODBC::VERSION = '1.58';
{
## no critic (ProhibitMagicNumbers ProhibitExplicitISA)
## no critic (ProhibitPackageVars)
package DBD::ODBC;
use DBI ();
use DynaLoader ();
use Exporter ();
@ISA = qw(Exporter DynaLoader);
# my $Revision = substr(q$Id$, 13,2);
require_version DBI 1.609;
bootstrap DBD::ODBC $VERSION;
$err = 0; # holds error code for DBI::err
$errstr = q{}; # holds error string for DBI::errstr
$sqlstate = "00000"; # starting state
$drh = undef; # holds driver handle once initialised
use constant {
# header fields in SQLGetDiagField:
SQL_DIAG_CURSOR_ROW_COUNT => -1249,
SQL_DIAG_DYNAMIC_FUNCTION => 7,
SQL_DIAG_DYNAMIC_FUNCTION_CODE => 12,
SQL_DIAG_NUMBER => 2,
SQL_DIAG_RETURNCODE => 1,
SQL_DIAG_ROW_COUNT => 3,
# record fields in SQLGetDiagField:
SQL_DIAG_CLASS_ORIGIN => 8,
SQL_DIAG_COLUMN_NUMBER => -1247,
SQL_DIAG_CONNECTION_NAME => 10,
SQL_DIAG_MESSAGE_TEXT => 6,
SQL_DIAG_NATIVE => 5,
SQL_DIAG_ROW_NUMBER => -1248,
SQL_DIAG_SERVER_NAME => 11,
SQL_DIAG_SQLSTATE => 4,
SQL_DIAG_SUBCLASS_ORIGIN => 9,
# TAF constants - these are just copies of Oracle constants
# events:
OCI_FO_END => 0x00000001,
OCI_FO_ABORT => 0x00000002,
OCI_FO_REAUTH => 0x00000004,
OCI_FO_BEGIN => 0x00000008,
OCI_FO_ERROR => 0x00000010,
# callback return codes:
OCI_FO_RETRY => 25410,
# types:
OCI_FO_NONE => 0x00000001,
OCI_FO_SESSION => 0x00000002,
OCI_FO_SELECT => 0x00000004,
OCI_FO_TXNAL => 0x00000008
};
our @EXPORT_DIAGS = qw(SQL_DIAG_CURSOR_ROW_COUNT SQL_DIAG_DYNAMIC_FUNCTION SQL_DIAG_DYNAMIC_FUNCTION_CODE SQL_DIAG_NUMBER SQL_DIAG_RETURNCODE SQL_DIAG_ROW_COUNT SQL_DIAG_CLASS_ORIGIN SQL_DIAG_COLUMN_NUMBER SQL_DIAG_CONNECTION_NAME SQL_DIAG_MESSAGE_TEXT SQL_DIAG_NATIVE SQL_DIAG_ROW_NUMBER SQL_DIAG_SERVER_NAME SQL_DIAG_SQLSTATE SQL_DIAG_SUBCLASS_ORIGIN);
our @EXPORT_TAF = qw(OCI_FO_END OCI_FO_ABORT OCI_FO_REAUTH OCI_FO_BEGIN OCI_FO_ERROR OCI_FO_RETRY OCI_FO_NONE OCI_FO_SESSION OCI_FO_SELECT OCI_FO_TXNAL);
our @EXPORT_OK = (@EXPORT_DIAGS, @EXPORT_TAF);
our %EXPORT_TAGS = (
diags => \@EXPORT_DIAGS,
taf => \@EXPORT_TAF);
sub parse_trace_flag {
my ($class, $name) = @_;
return 0x02_00_00_00 if $name eq 'odbcunicode';
return 0x04_00_00_00 if $name eq 'odbcconnection';
return DBI::parse_trace_flag($class, $name);
}
sub parse_trace_flags {
my ($class, $flags) = @_;
return DBI::parse_trace_flags($class, $flags);
}
my $methods_are_installed = 0;
sub driver{
return $drh if $drh;
my($class, $attr) = @_;
$class .= "::dr";
# not a 'my' since we use it above to prevent multiple drivers
$drh = DBI::_new_drh($class, {
'Name' => 'ODBC',
'Version' => $VERSION,
'Err' => \$DBD::ODBC::err,
'Errstr' => \$DBD::ODBC::errstr,
'State' => \$DBD::ODBC::sqlstate,
'Attribution' => 'DBD::ODBC by Jeff Urlwin, Tim Bunce and Martin J. Evans',
});
if (!$methods_are_installed) {
DBD::ODBC::st->install_method("odbc_lob_read");
DBD::ODBC::st->install_method("odbc_rows", { O=>0x00000000 });
DBD::ODBC::st->install_method("odbc_describe_param", { O=>0x00000000 });
# don't clear errors - IMA_KEEP_ERR = 0x00000004
DBD::ODBC::st->install_method("odbc_getdiagrec", { O=>0x00000004 });
DBD::ODBC::db->install_method("odbc_getdiagrec", { O=>0x00000004 });
DBD::ODBC::db->install_method("odbc_getdiagfield", { O=>0x00000004 });
DBD::ODBC::st->install_method("odbc_getdiagfield", { O=>0x00000004 });
$methods_are_installed++;
}
return $drh;
}
sub CLONE { undef $drh }
1;
}
{ package DBD::ODBC::dr; # ====== DRIVER ======
use strict;
use warnings;
## no critic (ProhibitBuiltinHomonyms)
sub connect {
my($drh, $dbname, $user, $auth, $attr)= @_;
#$user = q{} unless defined $user;
#$auth = q{} unless defined $auth;
# create a 'blank' dbh
my $this = DBI::_new_dbh($drh, {
'Name' => $dbname,
'USER' => $user,
'CURRENT_USER' => $user,
});
# Call ODBC _login func in Driver.xst file => dbd_db_login6
# and populate internal handle data.
# There are 3 versions (currently) if you have a recent DBI:
# dbd_db_login (oldest)
# dbd_db_login6 (with attribs hash & char * args) and
# dbd_db_login6_sv (as dbd_db_login6 with perl scalar args
DBD::ODBC::db::_login($this, $dbname, $user, $auth, $attr) or return;
return $this;
}
## use critic
sub data_sources {
my ($drh, $attr) = @_;
my $dsref = DBD::ODBC::dr::_data_sources( $drh, $attr );
if( defined( $dsref ) && ref( $dsref ) eq "ARRAY" ) {
return @$dsref;
}
return (); # Return empty array
}
}
{ package DBD::ODBC::db; # ====== DATABASE ======
use strict;
use warnings;
use constant SQL_DRIVER_HSTMT => 5;
use constant SQL_DRIVER_HLIB => 76;
use constant SQL_DRIVER_HDESC => 135;
sub parse_trace_flag {
my ($h, $name) = @_;
return DBD::ODBC->parse_trace_flag($name);
}
sub private_attribute_info {
return {
odbc_ignore_named_placeholders => undef, # sth and dbh
odbc_default_bind_type => undef, # sth and dbh
odbc_force_bind_type => undef, # sth and dbh
odbc_force_rebind => undef, # sth and dbh
odbc_async_exec => undef, # sth and dbh
odbc_exec_direct => undef,
odbc_describe_parameters => undef,
odbc_SQL_ROWSET_SIZE => undef,
odbc_SQL_DRIVER_ODBC_VER => undef,
odbc_cursortype => undef,
odbc_query_timeout => undef, # sth and dbh
odbc_has_unicode => undef,
odbc_out_connect_string => undef,
odbc_version => undef,
odbc_err_handler => undef,
odbc_putdata_start => undef, # sth and dbh
odbc_column_display_size => undef, # sth and dbh
odbc_utf8_on => undef, # sth and dbh
odbc_driver_complete => undef,
odbc_batch_size => undef,
odbc_array_operations => undef, # sth and dbh
odbc_taf_callback => undef,
odbc_trace => undef, # dbh
odbc_trace_file => undef, # dbh
};
}
sub prepare {
my($dbh, $statement, @attribs)= @_;
# create a 'blank' sth
my $sth = DBI::_new_sth($dbh, {
'Statement' => $statement,
});
# Call ODBC func in ODBC.xs file.
# (This will actually also call SQLPrepare for you.)
# and populate internal handle data.
DBD::ODBC::st::_prepare($sth, $statement, @attribs)
or return;
return $sth;
}
sub column_info {
my ($dbh, $catalog, $schema, $table, $column) = @_;
$catalog = q{} if (!$catalog);
$schema = q{} if (!$schema);
$table = q{} if (!$table);
$column = q{} if (!$column);
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLColumns" });
_columns($dbh,$sth, $catalog, $schema, $table, $column)
or return;
return $sth;
}
sub columns {
my ($dbh, $catalog, $schema, $table, $column) = @_;
$catalog = q{} if (!$catalog);
$schema = q{} if (!$schema);
$table = q{} if (!$table);
$column = q{} if (!$column);
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLColumns" });
_columns($dbh,$sth, $catalog, $schema, $table, $column)
or return;
return $sth;
}
sub table_info {
my ($dbh, $catalog, $schema, $table, $type) = @_;
if ($#_ == 1) {
my $attrs = $_[1];
$catalog = $attrs->{TABLE_CAT};
$schema = $attrs->{TABLE_SCHEM};
$table = $attrs->{TABLE_NAME};
$type = $attrs->{TABLE_TYPE};
}
# the following was causing a problem
# changing undef to '' makes a big difference to SQLTables
# as SQLTables has special cases for empty string calls
#$catalog = q{} if (!$catalog);
#$schema = q{} if (!$schema);
#$table = q{} if (!$table);
#$type = q{} if (!$type);
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLTables" });
DBD::ODBC::st::_tables($dbh,$sth, $catalog, $schema, $table, $type)
or return;
return $sth;
}
sub primary_key_info {
my ($dbh, $catalog, $schema, $table ) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLPrimaryKeys" });
$catalog = q{} if (!$catalog);
$schema = q{} if (!$schema);
$table = q{} if (!$table);
DBD::ODBC::st::_primary_keys($dbh,$sth, $catalog, $schema, $table )
or return;
return $sth;
}
sub statistics_info {
my ($dbh, $catalog, $schema, $table, $unique, $quick ) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLStatistics" });
$catalog = q{} if (!$catalog);
$schema = q{} if (!$schema);
$table = q{} if (!$table);
$unique = 1 if (!$unique);
$quick = 1 if (!$quick);
DBD::ODBC::st::_statistics($dbh, $sth, $catalog, $schema, $table,
$unique, $quick)
or return;
return $sth;
}
sub foreign_key_info {
my ($dbh, $pkcatalog, $pkschema, $pktable, $fkcatalog, $fkschema, $fktable ) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLForeignKeys" });
$pkcatalog = q{} if (!$pkcatalog);
$pkschema = q{} if (!$pkschema);
$pktable = q{} if (!$pktable);
$fkcatalog = q{} if (!$fkcatalog);
$fkschema = q{} if (!$fkschema);
$fktable = q{} if (!$fktable);
_GetForeignKeys($dbh, $sth, $pkcatalog, $pkschema, $pktable, $fkcatalog, $fkschema, $fktable) or return;
return $sth;
}
sub ping {
my $dbh = shift;
# DBD::Gofer does the following (with a 0 instead of "0") but it I
# cannot make it set a warning.
#return $dbh->SUPER::set_err("0", "can't ping while not connected") # warning
# unless $dbh->SUPER::FETCH('Active');
#my $pe = $dbh->FETCH('PrintError');
#$dbh->STORE('PrintError', 0);
my $evalret = eval {
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLTables_PING" })
or return 1;
my ($catalog, $schema, $table, $type);
$catalog = q{};
$schema = q{};
$table = 'NOXXTABLE';
$type = q{};
DBD::ODBC::st::_tables($dbh,$sth, $catalog, $schema, $table, $type)
or return 1;
$sth->finish;
return 0;
};
#$dbh->STORE('PrintError', $pe);
$dbh->set_err(undef,'',''); # clear any stored error from eval above
if ($evalret == 0) {
return 1;
} else {
return 0;
}
}
##### # saved, just for posterity.
##### sub oldping {
##### my $dbh = shift;
##### my $state = undef;
#####
##### # should never 'work' but if it does, that's okay!
##### # JLU incorporated patches from Jon Smirl 5/4/99
##### {
##### local $dbh->{RaiseError} = 0 if $dbh->{RaiseError};
##### # JLU added local PrintError handling for completeness.
##### # it shouldn't print, I think.
##### local $dbh->{PrintError} = 0 if $dbh->{PrintError};
##### my $sql = "select sysdate from dual1__NOT_FOUND__CANNOT";
##### my $sth = $dbh->prepare($sql);
##### # fixed "my" $state = below. Was causing problem with
##### # ping! Also, fetching fields as some drivers (Oracle 8)
##### # may not actually check the database for activity until
##### # the query is "described".
##### # Right now, Oracle8 is the only known version which
##### # does not actually check the server during prepare.
##### my $ok = $sth && $sth->execute();
#####
##### $state = $dbh->state;
##### $DBD::ODBC::err = 0;
##### $DBD::ODBC::errstr = "";
##### $DBD::ODBC::sqlstate = "00000";
##### return 1 if $ok;
##### }
##### return 1 if $state eq 'S0002'; # Base table not found
##### return 1 if $state eq '42S02'; # Base table not found.Solid EE v3.51
##### return 1 if $state eq 'S0022'; # Column not found
##### return 1 if $state eq '37000'; # statement could not be prepared (19991011, JLU)
##### # return 1 if $state eq 'S1000'; # General Error? ? 5/30/02, JLU. This is what Openlink is returning
##### # We assume that any other error means the database
##### # is no longer connected.
##### # Some special cases may need to be added to the code above.
##### return 0;
##### }
# New support for DBI which has the get_info command.
# leaving support for ->func(xxx, GetInfo) (below) for a period of time
# to support older applications which used this.
sub get_info {
my ($dbh, $item) = @_;
# Ignore some we cannot do
if ($item == SQL_DRIVER_HSTMT ||
$item == SQL_DRIVER_HLIB ||
$item == SQL_DRIVER_HDESC) {
return;
}
return _GetInfo($dbh, $item);
}
# new override of do method provided by Merijn Broeren
# this optimizes "do" to use SQLExecDirect for simple
# do statements without parameters.
## no critic (ProhibitBuiltinHomonyms)
sub do {
my($dbh, $statement, $attr, @params) = @_;
my $rows = 0;
## no critic (ProhibitMagicNumbers)
if( -1 == $#params ) {
$dbh->STORE(Statement => $statement);
# No parameters, use execute immediate
$rows = ExecDirect( $dbh, $statement );
if( 0 == $rows ) {
$rows = "0E0"; # 0 but true
} elsif( $rows < -1 ) {
undef $rows;
}
}
else
{
$rows = $dbh->SUPER::do( $statement, $attr, @params );
}
return $rows
}
## use critic
#
# can also be called as $dbh->func($sql, ExecDirect);
# if, for some reason, there are compatibility issues
# later with DBI's do.
#
sub ExecDirect {
my ($dbh, $sql) = @_;
return _ExecDirect($dbh, $sql);
}
# Call the ODBC function SQLGetInfo
# Args are:
# $dbh - the database handle
# $item: the requested item. For example, pass 6 for SQL_DRIVER_NAME
# See the ODBC documentation for more information about this call.
#
sub GetInfo {
my ($dbh, $item) = @_;
return get_info($dbh, $item);
}
# Call the ODBC function SQLStatistics
# Args are:
# See the ODBC documentation for more information about this call.
#
sub GetStatistics {
my ($dbh, $catalog, $schema, $table, $unique) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLStatistics" });
_GetStatistics($dbh, $sth, $catalog, $schema,
$table, $unique) or return;
return $sth;
}
# Call the ODBC function SQLForeignKeys
# Args are:
# See the ODBC documentation for more information about this call.
#
sub GetForeignKeys {
my ($dbh, $pk_catalog, $pk_schema, $pk_table,
$fk_catalog, $fk_schema, $fk_table) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLForeignKeys" });
_GetForeignKeys($dbh, $sth, $pk_catalog, $pk_schema, $pk_table,
$fk_catalog, $fk_schema, $fk_table) or return;
return $sth;
}
# Call the ODBC function SQLPrimaryKeys
# Args are:
# See the ODBC documentation for more information about this call.
#
sub GetPrimaryKeys {
my ($dbh, $catalog, $schema, $table) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLPrimaryKeys" });
_GetPrimaryKeys($dbh, $sth, $catalog, $schema, $table) or return;
return $sth;
}
# Call the ODBC function SQLSpecialColumns
# Args are:
# See the ODBC documentation for more information about this call.
#
sub GetSpecialColumns {
my ($dbh, $identifier, $catalog, $schema, $table, $scope, $nullable) = @_;
# create a "blank" statement handle
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLSpecialColumns" });
_GetSpecialColumns($dbh, $sth, $identifier, $catalog, $schema,
$table, $scope, $nullable) or return;
return $sth;
}
# sub GetTypeInfo {
# my ($dbh, $sqltype) = @_;
# # create a "blank" statement handle
# my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLGetTypeInfo" });
# # print "SQL Type is $sqltype\n";
# _GetTypeInfo($dbh, $sth, $sqltype) or return;
# return $sth;
# }
sub type_info_all {
my ($dbh, $sqltype) = @_;
$sqltype = DBI::SQL_ALL_TYPES unless defined $sqltype;
my $sth = DBI::_new_sth($dbh, { 'Statement' => "SQLGetTypeInfo" });
_GetTypeInfo($dbh, $sth, $sqltype) or return;
my $info = $sth->fetchall_arrayref;
unshift @{$info}, {
map { ($sth->{NAME}->[$_] => $_) } 0..$sth->{NUM_OF_FIELDS}-1
};
return $info;
}
}
{ package DBD::ODBC::st; # ====== STATEMENT ======
use strict;
use warnings;
*parse_trace_flag = \&DBD::ODBC::db::parse_trace_flag;
sub private_attribute_info {
return {
odbc_ignore_named_placeholders => undef, # sth and dbh
odbc_default_bind_type => undef, # sth and dbh
odbc_force_bind_type => undef, # sth and dbh
odbc_force_rebind => undef, # sth and dbh
odbc_async_exec => undef, # sth and dbh
odbc_query_timeout => undef, # sth and dbh
odbc_putdata_start => undef, # sth and dbh
odbc_column_display_size => undef, # sth and dbh
odbc_utf8_on => undef, # sth and dbh
odbc_exec_direct => undef, # sth and dbh
odbc_describe_parameters => undef, # sth and dbh
odbc_batch_size => undef, # sth and dbh
odbc_array_operations => undef, # sth and dbh
};
}
sub ColAttributes { # maps to SQLColAttributes
my ($sth, $colno, $desctype) = @_;
my $tmp = _ColAttributes($sth, $colno, $desctype);
return $tmp;
}
sub cancel {
my $sth = shift;
my $tmp = _Cancel($sth);
return $tmp;
}
sub execute_for_fetch {
my ($sth, $fetch_tuple_sub, $tuple_status) = @_;
#print "execute_for_fetch\n";
my $row_count = 0;
my $tuple_count="0E0";
my $tuple_batch_status;
my $batch_size = $sth->FETCH('odbc_batch_size');
$sth->trace_msg("execute_for_fetch($fetch_tuple_sub, " .
($tuple_status ? $tuple_status : 'undef') .
") batch_size = $batch_size\n", 4);
# Use DBI's execute_for_fetch if ours is disabled
my $override = (defined($ENV{ODBC_DISABLE_ARRAY_OPERATIONS}) ?
$ENV{ODBC_DISABLE_ARRAY_OPERATIONS} : -1);
if ((($sth->FETCH('odbc_array_operations') == 0) && ($override != 0)) ||
$override == 1) {
$sth->trace_msg("array operations disabled\n", 4);
my $sth = shift;
return $sth->SUPER::execute_for_fetch(@_);
}
$tuple_batch_status = [ ]; # we always want this here
if (defined($tuple_status)) {
@$tuple_status = ();
}
my $finished;
while (1) {
my @tuple_batch;
for (my $i = 0; $i < $batch_size; $i++) {
$finished = $fetch_tuple_sub->();
push @tuple_batch, [ @{$finished || last} ];
}
$sth->trace_msg("Found " . scalar(@tuple_batch) . " rows\n", 4);
last unless @tuple_batch;
my $res = odbc_execute_for_fetch($sth,
\@tuple_batch,
scalar(@tuple_batch),
$tuple_batch_status);
$sth->trace_msg("odbc_execute_array returns " .
($res ? $res : 'undef') . "\n", 4);
#print "odbc_execute_array XS returned $res\n";
# count how many tuples were used
# basically they are all used unless marked UNUSED
if ($tuple_batch_status) {
foreach (@$tuple_batch_status) {
$tuple_count++ unless $_ == 7; # SQL_PARAM_UNUSED
next if ref($_);
$_ = -1; # we don't know individual row counts
}
if ($tuple_status) {
push @$tuple_status, @$tuple_batch_status
if defined($tuple_status);
}
}
if (!defined($res)) { # error
$row_count = undef;
last;
} else {
$row_count += $res;
}
last if !$finished;
}
if (!wantarray) {
return undef if !defined $row_count;
return $tuple_count;
}
return (defined $row_count ? $tuple_count : undef, $row_count);
}
}
1;
__END__
=head1 NAME
DBD::ODBC - ODBC Driver for DBI
=for html
=head1 VERSION
This documentation refers to DBD::ODBC version 1.58.
=head1 WARNING
This version of DBD::ODBC contains a significant fix to unicode when
inserting into CHAR/VARCHAR columns and it is a change in behaviour
from 1.45. The change B applies to unicode builds of DBD::ODBC
(the default on Windows but you can build it for unicode on unix too)
and char/varchar columns and not nchar/nvarchar columns.
Prior to this release of DBD::ODBC when you are using the unicode
build of DBD::ODBC and inserted data into a CHAR/VARCHAR columns using
parameters DBD::ODBC did this:
1 if you set odbc_describe_parameters to 0, (thus preventing DBD::ODBC
from calling SQLDescribeParam) parameters for CHAR/VARCHAR columns
were bound as SQL_WVARCHAR or SQL_WLONGVARCHAR (depending on the
length of the parameter).
2 if you set odbc_force_bind_type then all parameters are bound as you
specified.
3 if you override the parameter type in the bind_param method, the
type you specified would be used.
4 if the driver does not support SQLDescribeParam or SQLDescribeParam
was called and failed then the bind type defaulted as in 1.
5 if none of the above (and I'd guess that is the normal case for most
people) then DBD::ODBC calls SQLDescribeParam to find the parameter
type. This usually returns SQL_CHAR or SQL_VARCHAR for CHAR/VARCHAR
columns unsurprisingly. The parameter was then bound as SQL_VARCHAR.
Items 1 to 4 still apply. 5 now has a different behaviour. In this
release, DBD::ODBC now looks at your bound data first before using the
type returned by SQLDescribeParam. If you data looks like unicode
(i.e., SvUTF8() is true) it now binds the parameter as SQL_WVARCHAR.
What might this might mean to you?
If you had Perl scalars that were bound to CHAR/VARCHAR columns in an
insert/update/delete and those scalars contained unicode, DBD::ODBC
would actually pass the individual octets in your scalar not
characters. For instance, if you had the Perl scalar "\x{20ac}" (the
Euro unicode character) and you bound it to a CHAR/VARCHAR, DBD::ODBC
would pass 0xe2, 0x82, 0xc2 as separate characters because those bytes
were Perl's UTF-8 encoding of a euro. These would probably be
interpreted by your database engine as 3 characters in its current
codepage. If you queried your database to find the length of the data
inserted you'd probably get back 3, not 1.
However, when DBD::ODBC read that column back in a select
statement, it would bind the column as SQL_WCHAR and you'd get back 3
characters with the utf8 flag on (what those characters were depends
on how your database or driver translates code page characters to wide
characters).
What should happen now is that if your bound parameters are unicode,
DBD::ODBC will bind them as wide characters (unicode) and your driver
or database will attempt to convert them into the code page it is
using. This means so long as your database can store the data you are
inserting, when you read it back you should get what you inserted.
=head1 SYNOPSIS
use DBI;
$dbh = DBI->connect('dbi:ODBC:DSN=mydsn', 'user', 'password');
See L for more information.
=head1 DESCRIPTION
=head2 Change log and FAQs
Please note that the change log has been moved to
DBD::ODBC::Changes. To access this documentation, use
C.
The FAQs have also moved to DBD::ODBC::FAQ.pm. To access the FAQs use
C.
=head2 Important note about the tests
DBD::ODBC is unlike most other DBDs in that it connects to literally
dozens of possible ODBC Drivers. It is practically impossible for me
to test every one and so some tests may fail with some ODBC Drivers.
This does not mean DBD::ODBC will not work with your ODBC Driver but
it is worth reporting any test failures on rt.cpan.org or to the
dbi-users mailing list.
=head2 DBI attribute handling
If a DBI defined attribute is not mentioned here it behaves as per the
DBI specification.
=head3 ReadOnly (boolean)
DBI documents the C attribute as being settable and
retrievable on connection and statement handles. In ODBC setting
ReadOnly to true causes the connection attribute C
to be set to C and setting it to false will set the
access mode to C (which is the default in ODBC).
B There is no equivalent of setting ReadOnly on a statement
handle in ODBC.
B See ODBC documentation on C as setting it
to C does B prevent your script from running
updates or deletes; it is simply a hint to the driver/database that
you won't being doing updates.
B Since DBD::ODCB 1.44_3, if the driver does not support
setting C and returns SQL_SUCCESS_WITH_INFO and
"option value changed" a warning is issued (which you'll only see if
you have DBI > 1.628). In addition, any subsequent attempts to fetch
the ReadOnly attribute will return the value last set.
This attribute requires DBI version 1.55 or better.
=head2 Private attributes common to connection and statement handles
=head3 odbc_ignore_named_placeholders
Use this if you have special needs (such as Oracle triggers, etc)
where :new or :name mean something special and are not just place
holder names. You B then use ? for binding parameters. Example:
$dbh->{odbc_ignore_named_placeholders} = 1;
$dbh->do("create trigger foo as if :new.x <> :old.x then ... etc");
Without this, DBD::ODBC will think :new and :old are placeholders for
binding and get confused.
=head3 odbc_default_bind_type
This value defaults to 0.
Older versions of DBD::ODBC assumed that the parameter binding type
was 12 (C). Newer versions always attempt to call
C to find the parameter types but if
C is unavailable DBD::ODBC falls back to a default
bind type. The internal default bind type is C (for
non-unicode build) and C or C (for a
unicode build depending on whether the parameter is unicode or
not). If you set C to a value other than 0 you
override the internal default.
B If you call the C method with a SQL type this
overrides everything else above.
=head3 odbc_force_bind_type
This value defaults to 0.
If set to anything other than 0 this will force bound parameters to be
bound as this type and C will not be used; in other
words it implies L is set to false too.
Older versions of DBD::ODBC assumed the parameter binding type was 12
(C) and newer versions always attempt to call
C to find the parameter types. If your driver
supports C and it succeeds it may still fail to
describe the parameters accurately (MS SQL Server sometimes does this
with some SQL like I. If, after that you
have some SQL where you need to vary the parameter types used add the
SQL type to the end of the C method.
use DBI qw(:sql_types);
$h = DBI->connect;
# set the default bound parameter type
$h->{odbc_default_bind_type} = SQL_VARCHAR;
# bind a parameter with a specific type
$s = $h->prepare(q/insert into mytable values(?)/);
$s->bind_param(1, "\x{263a}", SQL_WVARCHAR);
=head2 MS SQL Server Query Notification
Query notifications were introduced in SQL Server 2005 and SQL Server
Native Client. Query notifications allow applications to be notified
when data has changed.
DBD::ODBC supports query notification with MS SQL Server using the additional
prepare attributes odbc_qn_msgtxt, odbc_qn_options and odbc_qn_timeout. When
you pass suitable values for these attributes to the prepare method, DBD::ODBC
will make the appropriate SQLSetStmtAttr calls after the statement
has been allocated.
It is beyond the scope of this document to provide a tutorial on doing this
but here are some notes that might help you get started.
On SQL Server
create database MyDatabase
ALTER DATABASE MyDatabase SET ENABLE_BROKER
use MyDatabase
CREATE TABLE QNtest (a int NOT NULL PRIMARY KEY,
b nchar(5) NOT NULL,
c datetime NOT NULL)
INSERT QNtest (a, b, c) SELECT 1, 'ALFKI', '19991212'
CREATE QUEUE myQueue
CREATE SERVICE myService ON QUEUE myQueue
See L
You need to set these SQL Server permissions unless the subscriber is a sysadmin:
GRANT RECEIVE ON QueryNotificationErrorsQueue TO ""
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO ""
To subscribe to query notification for this example:
# Prepare the statement.
# This is the SQL you want to know if the result changes later
my $sth = $dbh->prepare(q/SELECT a, b, c FROM dbo.QNtest WHERE a = 1/,
{odbc_qn_msgtxt => 'Message text',
odbc_qn_options => 'service=myService',
odbc_qn_timeout=> 430000});
# Fetch and display the result set value.
while ( my @row = $sth->fetchrow_array ) {
print "@row\n";
}
# select * from sys.dm_qn_subscriptions will return a record now you are subscribed
To wait for notification:
# Avoid "String data, right truncation" error when retrieving
# the message.
$dbh->{LongReadLen} = 800;
# This query generates a result telling you which query has changed
# It will block until the timeout or the query changes
my $sth = $dbh->prepare(q/WAITFOR (RECEIVE * FROM MyQueue)/);
$sth->execute();
# in the mean time someone does UPDATE dbo.QNtest SET c = '19981212' WHERE a = 1
# Fetch and display the result set value.
while ( my @row = $sth->fetchrow_array ) {
print "@row\n";
}
# You now need to understand the result and look to decide which query has changed
=head2 Version Control
DBD::ODBC source code was under version control at svn.perl.org
until April 2013 when svn.perl.org was closed down and it is now on
github at https://github.com/perl5-dbi/DBD-ODBC.git.
=head2 Contributing
There are a number of ways you may help with the development and
maintenance of this module:
=over
=item Submitting patches
Please send me a git pull request or email a unified diff.
Please try and include a test which demonstrates the fix/change
working.
=item Reporting installs
Install CPAN::Reporter and report you installations. This is easy to
do - see L.
=item Report bugs
If you find what you believe is a bug then enter it into the
L system. Where
possible include code which reproduces the problem including any
schema required and the versions of software you are using.
If you are unsure whether you have found a bug report it anyway or
post it to the dbi-users mailing list.
=item pod comments and corrections
If you find inaccuracies in the DBD::ODBC pod or have a comment which
you think should be added then go to L and submit
them there. I get an email for every comment added and will review
each one and apply any changes to the documentation.
=item Review DBD::ODBC
Add your review of DBD::ODBC on L.
If you are a member on ohloh then add your review or register your
use of DBD::ODBC at L.
=item submit test cases
Most DBDs are built against a single client library for the database.
Unlike other DBDs, DBD::ODBC works with many different ODBC drivers.
Although they all should be written with regard to the ODBC
specification drivers have bugs and in some places the specification is
open to interpretation. As a result, when changes are applied to
DBD::ODBC it is very easy to break something in one ODBC driver.
What helps enormously to identify problems in the many combinations
of DBD::ODBC and ODBC drivers is a large test suite. I would greatly
appreciate any test cases and in particular any new test cases for
databases other than MS SQL Server.
=item Test DBD::ODBC
I have a lot of problems deciding when to move a development release
to an official release since I get few test reports for development
releases. What often happens is I call for testers on various lists,
get a few and then get inundated with requests to do an official
release. Then I do an official release and loads of rts appear out of
nowhere and the cycle starts again.
DBD::ODBC by its very nature works with many ODBC Drivers and it is
impossible for me to have and test them all (this differs from other
DBDs). If you depend on DBD::ODBC you should be interested in new
releases and if you send me your email address suggesting you are
prepared to be part of the DBD::ODBC testing network I will credit you
in the Changes file and perhaps the main DBD::ODBC file.
=back
=head2 CPAN Testers Reporting
Please, please, please (is that enough), consider installing
CPAN::Reporter so that when you install perl modules a report of the
installation success or failure can be sent to cpan testers. In this
way module authors 1) get feedback on the fact that a module is being
installed 2) get to know if there are any installation problems. Also
other people like you may look at the test reports to see how
successful they are before choosing the version of a module to
install.
See this guide on how to get started with sending test reports:
L.
=head2 Others/todo?
Level 2
SQLColumnPrivileges
SQLProcedureColumns
SQLProcedures
SQLTablePrivileges
SQLDrivers
SQLNativeSql
=head2 Random Links
These are in need of sorting and annotating. Some are relevant only
to ODBC developers.
You can find DBD::ODBC on ohloh now at:
L
If you use ohloh and DBD::ODBC please say you use it and rate it.
There is a good search engine for the various Perl DBI lists at the
following URLS:
L
L
L
L
L
For Linux/Unix folks, compatible ODBC driver managers can be found at:
L (unixODBC source and rpms)
L (iODBC driver manager source)
For Linux/Unix folks, you can checkout the following for ODBC Drivers and
Bridges:
L
L
L
L
=head2 Some useful tutorials:
Debugging Perl DBI:
L
Enabling ODBC support in Perl with Perl DBI and DBD::ODBC:
L
Perl DBI/DBD::ODBC Tutorial Part 1 - Drivers, Data Sources and Connection:
L
Perl DBI/DBD::ODBC Tutorial Part 2 - Introduction to retrieving data from your database:
L
Perl DBI/DBD::ODBC Tutorial Part 3 - Connecting Perl on UNIX or Linux to Microsoft SQL Server:
L
Perl DBI - Put Your Data On The Web:
L
Multiple Active Statements (MAS) and DBD::ODBC
L
64-bit ODBC
L
How do I insert Unicode supplementary characters into SQL Server from Perl?
L
Some Common Unicode Problems and Solutions using Perl DBD::ODBC and MS SQL Server
L
and a version possibly kept more up to date:
L
How do I use SQL Server Query Notifications from Linux and UNIX?
L
=head2 Frequently Asked Questions
Frequently asked questions are now in L. Run
C to view them.
=head1 CONFIGURATION AND ENVIRONMENT
You should consult the documentation for the ODBC Driver Manager
you are using.
=head1 DEPENDENCIES
L
L
=head1 INCOMPATIBILITIES
None known.
=head1 BUGS AND LIMITATIONS
None known other than the deviations from the DBI specification mentioned
above in L.
Please report any to me via the CPAN RT system. See
L for more details.
=head1 AUTHOR
Tim Bunce
Jeff Urlwin
Thomas K. Wenrich
Martin J. Evans
=head1 LICENSE AND COPYRIGHT
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. See L. This
program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.
Portions of this software are Copyright Tim Bunce, Thomas K. Wenrich,
Jeff Urlwin and Martin J. Evans - see the source.
=head1 SEE ALSO
L
DBD::ODBC can be used with many ODBC drivers to many different
databases. If you want a generic DBD for multiple databases DBD::ODBC
is probably for you. If you are only accessing a single database then
you might want to look for DBD::my_database (e.g. DBD::Oracle) as
database specific DBDs often have more functionality.
L or L for logging DBI method calls, SQL,
parameters and results.
=cut