# \DTLdeletedb (from datatool package) has no effect within \foreach

This question led to a new feature in a package:
datatool

\DTLgdeletedb is available in (v2.13) and later.

I am trying to use a \foreach loop to iterate over a few items. For each iteration of the steps of \ExtractData are:

1. Load the data into mydb.
2. Extract the appropriate data.
3. Delete mydb.

This works just fine outside of a \foreach. But if used within a \foreach, it appears that the database is not deleted, and any subsequent attempt to create a new database with the same name fails with:

Package datatool Error: Database myDB' already exists.

## Notes:

• The MWE example below, shows that that database access is working fine outside of a \foreach, and within the very first invocation within a \foreach.
• The failure occurs only if an attempt is made to use \ExtractData after a use within the \foreach. So uncommenting any of the commented blocks will show the failure.
• Before someone asks why I close and re-open the same db, in my actual usage, the data file opened is different for each member of the \foreach. Just the name of the data base is the same. I guess I could create a new name each time, but I'd really like to know what the actual problem is.

## Code:

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents*}{foo.dat}
Smith, John, 1977-05-04
Doe,   Jane, 1980-05-04
\end{filecontents*}

\usepackage{xstring}
\usepackage{pgffor}
\usepackage{datatool}

\newcommand{\ExtractData}[3]{% #1=file name #2=row, #3=col
\IfEq{\DTLrowcount{myDB}}{0}{%
\def\Value{NO VALUE}
}{%
\DTLgetvalue{\Value}{myDB}{#2}{#3}%
}%
\DTLdeletedb{myDB}%
\Value%
}

\begin{document}
\section{Works}
\ExtractData{foo.dat}{1}{2}
\ExtractData{foo.dat}{1}{1}

\section{Works in foreach (one element)}
\foreach \x in {2}{
\ExtractData{foo.dat}{1}{\x}
}

\section{Does NOT work subsequent to foreach (one element)}

%\ExtractData{foo.dat}{1}{1}

%\foreach \x in {1}{
%   \ExtractData{foo.dat}{1}{\x}
%}

\section{Does NOT works in foreach (more than one element)}
%\foreach \x in {2,1}{
%   \ExtractData{foo.dat}{1}{\x}\par\noindent
%}
\end{document}

• It's the usual problem: \foreach works in a group, so the \DTLdeletedb inside the \foreach deletes the database only locally. – egreg Apr 14 '12 at 9:50
• @egreg: I did think of that but since \DTLloaddb and \DTLdeletedb were within the same scope I though they would either both load/delete locally or both load/delete globally so did not think that that was the problem. – Peter Grill Apr 14 '12 at 17:28
• AFAIK, each step of \foreach is run in a distinct group. – egreg Apr 14 '12 at 17:33

\foreach works inside a group, so the deletion of the database is only local. Define a "global" deletion command \DTLgdeletedb and use that.

\makeatletter
\newcommand*{\DTLgdeletedb}[1]{%
\DTLifdbexists{#1}%
{%
{%
\global\expandafter\let\csname dtl@ci@#1@\@dtl@key\endcsname\undefined
}%
\global\expandafter\let\csname dtldb@#1\endcsname\undefined
\global\expandafter\let\csname dtlkeys@#1\endcsname\undefined
\global\expandafter\let\csname dtlrows@#1\endcsname\undefined
\global\expandafter\let\csname dtlcols@#1\endcsname\undefined
}%
{%
\PackageError{Can't delete database #1':
database doesn't exist}{}{}%
}%
}
\makeatother


It's just the same as the \DTLdeletedb command, but with \global prepended. I'm afraid that heavy use of this approach can pollute the save stack; be warned.

• I'll add this (and a global version of \DTLcleardb) to the next version of datatool. (You can track the progress of this feature using my feature tracker.) – Nicola Talbot Dec 10 '12 at 8:51

I can't answer this question but I want to mention that the loop \@for works well:

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents*}{foo.dat}
Smith, John, 1977-05-04
Doe,   Jane, 1980-05-04
\end{filecontents*}

\usepackage{xstring}
\usepackage{pgffor}
\usepackage{datatool}

\newcommand{\ExtractData}[3]{% #1=file name #2=row, #3=col
\IfEq{\DTLrowcount{myDB}}{0}{%
\def\Value{NO VALUE}
}{%
\DTLgetvalue{\Value}{myDB}{#2}{#3}%
}%
\DTLdeletedb{myDB}%
\Value%
}
\makeatletter
\let\myloop\@for
\makeatother
\begin{document}
\section{Works}
\ExtractData{foo.dat}{1}{2}
\ExtractData{foo.dat}{1}{1}

\section{Works in foreach (one element)}

\myloop\xx:={2}\do{%
\ExtractData{foo.dat}{1}{\xx}%
}

\section{Does NOT work subsequent to foreach (one element)}

\ExtractData{foo.dat}{1}{1}

\myloop\xx:={1}\do{%
\ExtractData{foo.dat}{1}{\xx}%
}

\section{Does NOT works in foreach (more than one element)}
\myloop\xx:={2,1}\do{%
\ExtractData{foo.dat}{1}{\xx}\par\noindent%
}
\end{document}

• Thanks for this. Will keep the @for loop in mind next time I get into trouble with a \foreach, which I am sure will happen. :-) – Peter Grill Apr 14 '12 at 17:35