Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

Reference:

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
    \DTLloaddb[noheader,keys={LastName, FirstName, DOB}]{myDB}{#1}%
    \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}
share|improve this question
1  
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
add comment

2 Answers

up vote 4 down vote accepted

\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}%
  {%
    \dtlforeachkey(\@dtl@key,\@dtl@col,\@dtl@type,\@dtl@head)\in{#1}\do
    {%
      \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.

share|improve this answer
1  
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
add comment

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
    \DTLloaddb[noheader,keys={LastName, FirstName, DOB}]{myDB}{#1}%
    \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}
share|improve this answer
    
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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.