DBIx-TempDB

 view release on metacpan or  search on metacpan

lib/DBIx/TempDB/Util.pm  view on Meta::CPAN

sub parse_sql {
  my ($type, $sql) = @_;
  $type = $type->canonical_engine if blessed $type;
  return _parse_mysql($sql) if $type eq 'mysql';
  return $sql;
}

sub _dsn_for_mysql {
  my ($url, $database_name) = @_;
  my %opt = %{$url->query_form_hash};
  my ($dsn, @userinfo);

  $url = URI::db->new($url);
  $url->dbname($database_name);
  $url->query(undef);
  $dsn      = $url->dbi_dsn;
  @userinfo = ($url->user, $url->password);

  $opt{AutoCommit}          //= 1;
  $opt{AutoInactiveDestroy} //= 1;
  $opt{PrintError}          //= 0;
  $opt{RaiseError}          //= 1;
  $opt{mysql_enable_utf8}   //= 1;

  return $dsn, @userinfo[0, 1], \%opt;
}

sub _dsn_for_pg {
  my ($url, $database_name) = @_;
  my %opt = %{$url->query_form_hash};
  my ($dsn, @userinfo);

  $url = URI::db->new($url);
  $url->dbname($database_name);
  $url->query(undef);
  if (my $service = delete $opt{service}) { $url->query_param(service => $service) }
  $dsn      = $url->dbi_dsn;
  @userinfo = ($url->user, $url->password);

  $opt{AutoCommit}          //= 1;
  $opt{AutoInactiveDestroy} //= 1;
  $opt{PrintError}          //= 0;
  $opt{RaiseError}          //= 1;

  return $dsn, @userinfo[0, 1], \%opt;
}

sub _dsn_for_sqlite {
  my ($url, $database_name) = @_;
  my %opt = %{$url->query_form_hash};

  $url = URI::db->new($url);
  $url->dbname($database_name);
  $url->query(undef);
  my $dsn = $url->dbi_dsn;

  $opt{AutoCommit}          //= 1;
  $opt{AutoInactiveDestroy} //= 1;
  $opt{PrintError}          //= 0;
  $opt{RaiseError}          //= 1;
  $opt{sqlite_unicode}      //= 1;

  return $dsn, "", "", \%opt;
}

sub _on_process_end_double_fork {
  my $code = shift;
  my $ppid = $$;

  warn "[TempDB:$$] Watching process using double fork.\n" if DEBUG;
  local $SIG{CHLD} = 'DEFAULT';
  pipe(my ($READER), my ($WRITER)) or confess "Couldn't create pipe: $!";

  # Parent
  if (my $pid_1 = fork // confess "Couldn't fork: $!") {
    my $pid_2;

    # Wait around until the second fork is done so that when we return from
    # here there are no new child processes that could mess things up if the
    # calling process does any process handling.
    close $WRITER;
    $pid_2 = <$READER>;
    $pid_2 = $pid_2 =~ m!(\d+)! ? $1 : undef;
    waitpid $pid_1, 0;
    confess "Couldn't get pid_2 from $pid_1." unless $pid_2;
    warn "[TempDB:$$] Double forked from $$ to $pid_1 to $pid_2\n" if DEBUG;
    return DBIx::TempDB::Guard->new(sub { kill TERM => $pid_2 }, $pid_2);
  }

  # Child #1
  # Detach completely from parent by creating our own session and process
  # group, closing all filehandles and forking a second time.
  $0 = "dbix-on-process-end-$ppid";
  close $READER;
  $DB::CreateTTY = 0;
  POSIX::setsid() != -1 or die "[TempDB:$$] Couldn't become session leader: $!\n";

  if (my $pid_2 = fork // die "[TempDB:$$] Couldn't fork: $!") {
    print $WRITER "$pid_2\n";
    close $WRITER;
    POSIX::_exit(0);
  }

  # Child #2
  warn "[TempDB:$ppid/$$] Double fork waiting on signals or parent to go away.\n" if DEBUG;
  _on_process_signals($code);
  sleep KILL_SLEEP_INTERVAL while kill 0, $ppid;
  local $ENV{DBIX_TEMP_DB_SIGNAL} = 'parent';
  $code->();
  exit;
}

sub _on_process_end_fork {
  my $code = shift;
  my $ppid = $$;

  # Parent
  warn "[TempDB:$$] Watching process using single fork.\n" if DEBUG;
  pipe(my ($READER), my ($WRITER)) or confess "Couldn't create pipe: $!";
  defined(my $pid = fork)          or confess "Couldn't fork: $!";
  return DBIx::TempDB::Guard->new(sub { close $WRITER }, $pid) if $pid;



( run in 1.961 second using v1.01-cache-2.11-cpan-39bf76dae61 )