Skip to content

Commit

Permalink
Merge 'optional-libfbembed' into master
Browse files Browse the repository at this point in the history
Closes mariuz#21. First-grade embedded support available now

Conflicts:
	Makefile.PL -- compiler flags and objects
  • Loading branch information
real-dam committed Oct 28, 2011
2 parents 47d16e5 + f0eed6b commit b760923
Show file tree
Hide file tree
Showing 49 changed files with 2,144 additions and 1,512 deletions.
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -6,7 +6,6 @@ MYMETA.*
MANIFEST
pm_to_blib
blib/
inc/
*.tmp
*~
*.log
Expand Down
126 changes: 126 additions & 0 deletions Firebird.pm
Expand Up @@ -98,6 +98,34 @@ sub _OdbcParse($$$)
$hash->{database} = "$hash->{host}:$hash->{database}" if $hash->{host};
}

sub create_database {
my ( $self, $params ) = ( shift, shift );
$self and $params and ref($params) and ref($params) eq 'HASH' and not @_
or croak 'Usage: '
. __PACKAGE__
. '->create_database( { params...} )';

exists $params->{db_path} and defined( $params->{db_path} )
or croak "Required parameter 'db_path' not supplied";

for( qw(db_path user password character_set) ) {
next unless exists $params->{$_};

$params->{$_} =~ s/'/''/g if defined($params->{$_});
}

DBD::Firebird::db::_create_database($params);
}

sub gfix {
my ( $self, $params ) = ( shift, shift );
$self and $params and ref($params) and ref($params) eq 'HASH' and not @_
or croak 'Usage: '
. __PACKAGE__
. '->gfix( { params...} )';

DBD::Firebird::db::_gfix($params);
}

package DBD::Firebird::dr;

Expand Down Expand Up @@ -1124,6 +1152,15 @@ Retrieve query plan from a prepared SQL statement.
my $sth = $dbh->prepare('SELECT * FROM foo');
print $sth->func('ib_plan'); # PLAN (FOO NATURAL)
=item C<ib_drop_database>
$result = $dbh->func('ib_drop_database');
Drops the database, associated with the connection. The database handle is no
longer valid after calling this function.
Caution is advised as the drop is irrevocable.
=back
Expand Down Expand Up @@ -1174,6 +1211,95 @@ C<Tie::DBI>. C<Tie::DBI> calls $dbh->prepare("LISTFIELDS $table_name") on
which Firebird fails to parse. I think that the call should be made within
an eval block.
=head1 SERVICE METHODS
=head2 DBD::Firebird->create_database( { params... } )
A class method for creating empty databases.
The method croaks on error. Params may be:
=over
=item db_path (string, required)
Path to database, including host name if necessary.
Examples:
=over
=item server:/path/to/db.fdb
=item /srv/db/base.fdb
=back
=item user (string, optional)
User name to be used for the request.
=item password (string, optional)
Password to be used for the request.
=item page_size (integer, optional)
Page size of the newly created database. Should be something supported by the
server. Firebird 2.5 supports the following page sizes: 1024, 2048, 4096, 8192
and 16384 and defaults to 4096.
=item character_set (string, optional)
The default character set of the database. Firebird 2.5 defaults to C<NONE>.
=item dialect (integer, optional)
The dialect of the database. Defaults to 3.
=back
After creation, the new database can be used after connecting to it with the
usual DBI->connect(...)
=head2 DBD::Firebird->gfix( { params } )
A class method for simulating a subset of the functionality of the
Firebird's L<gfix(1)> utility.
Params is a hash reference, with the following keys:
=over
=item db_path (string, required)
The path to the database to connect to. Should include host name if
necessary.
=item user (string, optional)
User name to connect as. Must be SYSDBA or database owner.
=item password (string, optional)
Password to be used for the connection.
Note that user and password are not needed for embedded connections.
=item forced_writes (boolean, optional)
If given, sets the forced writes flag of the database, causing Firebird
to use synchronous writes when working with that database.
=item buffers (integer, optional)
If given, sets the default number of buffers for the database. Can
be overriden on connect time. Note that buffers are measured in database
pages, not bytes.
=back
=head1 FAQ
=head2 Why do some operations performing positioned update and delete fail when AutoCommit is on?
Expand Down
218 changes: 218 additions & 0 deletions Firebird.xs
Expand Up @@ -1170,6 +1170,43 @@ ib_database_info(dbh, ...)
#undef DB_INFOBUF
#undef DB_RESBUF_CASEHDR

int
ib_drop_database(dbh)
SV *dbh
PREINIT:
ISC_STATUS status[ISC_STATUS_LENGTH];
CODE:
{
D_imp_dbh(dbh);

/* set the database handle to inactive */
DBIc_ACTIVE_off(imp_dbh);

/* rollback */
if (imp_dbh->tr)
{
isc_rollback_transaction(status, &(imp_dbh->tr));
if (ib_error_check(dbh, status))
XSRETURN(FALSE);

imp_dbh->tr = 0L;
}

FREE_SETNULL(imp_dbh->ib_charset);
FREE_SETNULL(imp_dbh->tpb_buffer);
FREE_SETNULL(imp_dbh->dateformat);
FREE_SETNULL(imp_dbh->timeformat);
FREE_SETNULL(imp_dbh->timestampformat);

/* drop */
isc_drop_database(status, &(imp_dbh->db));

if (ib_error_check(dbh, status)) RETVAL = 0;
else RETVAL = 1;
}
OUTPUT:
RETVAL

#*******************************************************************************

IB_EVENT *
Expand Down Expand Up @@ -1347,6 +1384,187 @@ ib_wait_event(dbh, ev)
OUTPUT:
RETVAL

void
_create_database(params)
HV *params
CODE:
{
ISC_STATUS status[ISC_STATUS_LENGTH]; /* isc api status vector */
char *str;
size_t len;
int page_size;
SV *sql, **sv;
unsigned short dialect;
isc_db_handle db = 0;
isc_tr_handle tr = 0;

sv = hv_fetch(params, "db_path", 7, FALSE);
if ((sv == NULL) || !SvOK(*sv))
croak("Missing db_path");

sql = sv_2mortal( newSVpv( "CREATE DATABASE '", 0 ) );
str = SvPV( *sv, len );
sv_catpvn( sql, str, len );
sv_catpvn( sql, "'", 1 );

sv = hv_fetch( params, "user", 4, FALSE );
if ( (sv != NULL) && SvOK(*sv) ) {
str = SvPV( *sv, len );
sv_catpvn( sql, " USER '", 7 );
sv_catpvn( sql, str, len );
sv_catpvn( sql, "'", 1 );
}

sv = hv_fetch( params, "password", 8, FALSE );
if ( (sv != NULL) && SvOK(*sv) ) {
str = SvPV( *sv, len );
sv_catpvn( sql, " PASSWORD '", 11 );
sv_catpvn( sql, str, len );
sv_catpvn( sql, "'", 1 );
}

sv = hv_fetch( params, "page_size", 9, FALSE );
if ( (sv != NULL) && SvOK(*sv) ) {
page_size = SvIV(*sv);
sv_catpvf( sql, " PAGE_SIZE %d", page_size );
}

sv = hv_fetch( params, "character_set", 13, FALSE );
if ( (sv != NULL) && SvOK(*sv) ) {
str = SvPV_nolen(*sv);
sv_catpvf( sql, " DEFAULT CHARACTER SET %s", str );
}

sv = hv_fetch( params, "dialect", 7, FALSE );
if ( (sv != NULL) && SvOK(*sv) ) {
dialect = SvIV(*sv);
}
else {
dialect = DEFAULT_SQL_DIALECT;
}

str = SvPV(sql, len);

isc_dsql_execute_immediate(
status,
&db, &tr,
len, str,
dialect, NULL );

if( (str = ib_error_decode(status)) != NULL ) {
croak(str);
}

// disconnect from the just created database
isc_detach_database( status, &db );
if ( (str = ib_error_decode(status)) != NULL ) {
warn(str);
}
}

void
_gfix(params)
HV *params
CODE:
{
ISC_STATUS status[ISC_STATUS_LENGTH]; /* isc api status vector */
char *db_path;
size_t db_path_len;
unsigned short buffers = 0;
short forced_writes = -1;
char *user = NULL, *pwd = NULL;
size_t user_len, pwd_len;
char ISC_FAR *dpb_buffer, *dpb;
short buflen = 0;
SV **sv;
isc_db_handle db = 0;
char *str;

sv = hv_fetch(params, "db_path", 7, FALSE);
if ((sv == NULL) || !SvOK(*sv))
croak("Missing db_path");

db_path = SvPV(*sv, db_path_len);

if (( (sv = hv_fetch(params, "user", 4, FALSE)) != NULL) && SvOK(*sv)) {
user = SvPV(*sv, user_len);
DPB_PREP_STRING_LEN(buflen, user_len);
}

if (( (sv = hv_fetch(params, "password", 8, FALSE)) != NULL) && SvOK(*sv)) {
pwd = SvPV(*sv, pwd_len);
DPB_PREP_STRING_LEN(buflen, pwd_len);
}

/* the actual interesting stuff -- database parameters */

if (((sv = hv_fetch(params, "buffers", 7, FALSE)) != NULL) && SvOK(*sv))
{
buffers = (unsigned short) SvIV(*sv);
DPB_PREP_INTEGER(buflen);
}

if (((sv = hv_fetch(params, "forced_writes", 13, FALSE)) != NULL) && SvOK(*sv))
{
forced_writes = SvTRUE(*sv) ? 1 : 0;
DPB_PREP_INTEGER(buflen);
}

/* add length of other parameters to needed buflen */
buflen += 1; /* dbpversion */

/* Allocate DPB */
Newx(dpb_buffer, buflen, char);

/* Fill DPB */
dpb = dpb_buffer;
*dpb++ = isc_dpb_version1;

if ( user != NULL ) {
DPB_FILL_STRING_LEN(dpb, isc_dpb_user_name, user, user_len);
}

if ( pwd != NULL ) {
DPB_FILL_STRING_LEN(dpb, isc_dpb_password, pwd, pwd_len);
}

if (buffers)
{
DPB_FILL_INTEGER(dpb, isc_dpb_num_buffers, buffers);
}

if (forced_writes >= 0)
{
DPB_FILL_INTEGER(dpb, isc_dpb_force_write, forced_writes);
}

if ( (dpb-dpb_buffer) != buflen ) {
fprintf(stderr, "# gfix: DPB length mismatch: %ld != %d\n", dpb-dpb_buffer, buflen);
fflush(stderr);
abort();
}

isc_attach_database(status, /* status vector */
db_path_len,
db_path,
&db,
buflen,
dpb_buffer);

/* freeing database parameter buffer */
Safefree(dpb_buffer);

/* return false on failed attach */
if ( ( str = ib_error_decode(status)) != NULL )
croak("gfix: %s", str);

// disconnect from the just created database
isc_detach_database( status, &db );
if ( (str = ib_error_decode(status)) != NULL ) {
warn("gfix/detach: %s", str);
}
}


MODULE = DBD::Firebird PACKAGE = DBD::Firebird::Event
PROTOTYPES: DISABLE
Expand Down

0 comments on commit b760923

Please sign in to comment.