From a7b5ed72c52e462c75a2fed485ed65aaf09a1d39 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 7 Aug 2010 13:47:33 -0700 Subject: [PATCH] SAVE: Fix UNSELECTED=RETAIN with a filter variable. Apparently this had never been tested. It assert-failed consistently because the filter variable being saved was from the dictionary before reading the data, but the dictionary after reading the data was different due to the temporary transformation used to drop the filtered data. --- src/data/procedure.c | 19 ++++++++++++++++--- src/data/procedure.h | 3 ++- src/language/data-io/save.c | 11 ++--------- tests/automake.mk | 1 + tests/language/data-io/save.at | 31 +++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 tests/language/data-io/save.at diff --git a/src/data/procedure.c b/src/data/procedure.c index c79e784e..fa52806f 100644 --- a/src/data/procedure.c +++ b/src/data/procedure.c @@ -163,10 +163,14 @@ proc_execute (struct dataset *ds) static const struct casereader_class proc_casereader_class; -/* Opens dataset DS for reading cases with proc_read. +/* Opens dataset DS for reading cases with proc_read. If FILTER is true, then + cases filtered out with FILTER BY will not be included in the casereader + (which is usually desirable). If FILTER is false, all cases will be + included regardless of FILTER BY settings. + proc_commit must be called when done. */ struct casereader * -proc_open (struct dataset *ds) +proc_open_filtering (struct dataset *ds, bool filter) { struct casereader *reader; @@ -179,7 +183,8 @@ proc_open (struct dataset *ds) /* Finish up the collection of transformations. */ add_case_limit_trns (ds); - add_filter_trns (ds); + if (filter) + add_filter_trns (ds); trns_chain_finalize (ds->cur_trns_chain); /* Make permanent_dict refer to the dictionary right before @@ -237,6 +242,14 @@ proc_open (struct dataset *ds) return reader; } +/* Opens dataset DS for reading cases with proc_read. + proc_commit must be called when done. */ +struct casereader * +proc_open (struct dataset *ds) +{ + return proc_open_filtering (ds, true); +} + /* Returns true if a procedure is in progress, that is, if proc_open has been called but proc_commit has not. */ bool diff --git a/src/data/procedure.h b/src/data/procedure.h index 09d44eee..bb4dad2b 100644 --- a/src/data/procedure.h +++ b/src/data/procedure.h @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -72,6 +72,7 @@ void proc_discard_output (struct dataset *ds); bool proc_execute (struct dataset *ds); time_t time_of_last_procedure (struct dataset *ds); +struct casereader *proc_open_filtering (struct dataset *, bool filter); struct casereader *proc_open (struct dataset *); bool proc_is_open (const struct dataset *); bool proc_commit (struct dataset *); diff --git a/src/language/data-io/save.c b/src/language/data-io/save.c index c34d4462..91fbc2fd 100644 --- a/src/language/data-io/save.c +++ b/src/language/data-io/save.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,7 +105,6 @@ parse_output_proc (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type) { bool retain_unselected; - struct variable *saved_filter_variable; struct casewriter *output; bool ok; @@ -114,16 +113,10 @@ parse_output_proc (struct lexer *lexer, struct dataset *ds, if (output == NULL) return CMD_CASCADING_FAILURE; - saved_filter_variable = dict_get_filter (dataset_dict (ds)); - if (retain_unselected) - dict_set_filter (dataset_dict (ds), NULL); - - casereader_transfer (proc_open (ds), output); + casereader_transfer (proc_open_filtering (ds, !retain_unselected), output); ok = casewriter_destroy (output); ok = proc_commit (ds) && ok; - dict_set_filter (dataset_dict (ds), saved_filter_variable); - return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; } diff --git a/tests/automake.mk b/tests/automake.mk index 1d247351..a2ceef6b 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -419,6 +419,7 @@ EXTRA_DIST += \ TESTSUITE_AT = \ tests/data/calendar.at \ tests/language/data-io/data-list.at \ + tests/language/data-io/save.at \ tests/language/dictionary/mrsets.at \ tests/language/expressions/evaluate.at \ tests/language/stats/aggregate.at \ diff --git a/tests/language/data-io/save.at b/tests/language/data-io/save.at new file mode 100644 index 00000000..28a186ff --- /dev/null +++ b/tests/language/data-io/save.at @@ -0,0 +1,31 @@ +AT_BANNER([SAVE]) + +# UNSELECTED=DELETE used to cause a crash if there was actually a +# filter variable. +AT_SETUP([SAVE -- delete unselected]) +AT_DATA([data.txt], [dnl +0 '1 9:30:05' 1/2/2003 "25/8/1995 15:30:00" "'a,b,c'",0 +, '-0 5:17' 10/31/2010 "9/4/2008 9:29:00" " xxx ",1 +1.625,'0 12:00',,,xyzzy,1 +]) +AT_DATA([save.pspp], [dnl +SET DECIMAL=DOT. +DATA LIST LIST NOTABLE FILE="data.txt" + /number(F8.3) time(DTIME10) date(ADATE10) datetime(DATETIME20) string(A8) + filter(F1.0). +MISSING VALUES number(0) time('0 12:00') string('xyzzy'). +FILTER BY filter. +SAVE /OUTFILE="data.sav" /UNSELECTED=DELETE. +]) +AT_DATA([get.pspp], [dnl +GET FILE='data.sav'. +LIST. +]) +AT_CHECK([pspp -O format=csv save.pspp]) +AT_CHECK([pspp -O format=csv get.pspp], [0], [dnl +Table: Data List +number,time,date,datetime,string,filter +. ,-0 05:17,10/31/2010,09-APR-2008 09:29:00,xxx ,1 +1.625,0 12:00:00,.,.,xyzzy ,1 +]) +AT_CLEANUP -- 2.30.2