/* 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
#include <config.h>
+#include "data/procedure.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include <data/case.h>
-#include <data/case-map.h>
-#include <data/caseinit.h>
-#include <data/casereader.h>
-#include <data/casereader-provider.h>
-#include <data/casewriter.h>
-#include <data/dictionary.h>
-#include <data/file-handle-def.h>
-#include <data/procedure.h>
-#include <data/transformations.h>
-#include <data/variable.h>
-#include <libpspp/deque.h>
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-#include <libpspp/taint.h>
-#include <libpspp/i18n.h>
-
-#include "xalloc.h"
+#include "data/case.h"
+#include "data/case-map.h"
+#include "data/caseinit.h"
+#include "data/casereader.h"
+#include "data/casereader-provider.h"
+#include "data/casereader-shim.h"
+#include "data/casewriter.h"
+#include "data/dictionary.h"
+#include "data/file-handle-def.h"
+#include "data/transformations.h"
+#include "data/variable.h"
+#include "libpspp/deque.h"
+#include "libpspp/misc.h"
+#include "libpspp/str.h"
+#include "libpspp/taint.h"
+#include "libpspp/i18n.h"
+
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
struct dataset {
/* Cases are read from source,
but proc_commit not yet called. */
}
proc_state;
- casenumber cases_written; /* Cases output so far. */
- bool ok; /* Error status. */
+ casenumber cases_written; /* Cases output so far. */
+ bool ok; /* Error status. */
+ struct casereader_shim *shim; /* Shim on proc_open() casereader. */
void (*callback) (void *); /* Callback for when the dataset changes */
void *cb_data;
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;
+
assert (ds->source != NULL);
assert (ds->proc_state == PROC_COMMITTED);
/* 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
{
struct dictionary *pd = ds->permanent_dict;
size_t compacted_value_cnt = dict_count_values (pd, 1u << DC_SCRATCH);
- bool should_compact = compacted_value_cnt < dict_get_next_value_idx (pd);
- ds->compactor = (should_compact
- ? case_map_to_compact_dict (pd, 1u << DC_SCRATCH)
- : NULL);
- ds->sink = autopaging_writer_create (compacted_value_cnt);
+ if (compacted_value_cnt < dict_get_next_value_idx (pd))
+ {
+ struct caseproto *compacted_proto;
+ compacted_proto = dict_get_compacted_proto (pd, 1u << DC_SCRATCH);
+ ds->compactor = case_map_to_compact_dict (pd, 1u << DC_SCRATCH);
+ ds->sink = autopaging_writer_create (compacted_proto);
+ caseproto_unref (compacted_proto);
+ }
+ else
+ {
+ ds->compactor = NULL;
+ ds->sink = autopaging_writer_create (dict_get_proto (pd));
+ }
}
else
{
/* FIXME: use taint in dataset in place of `ok'? */
/* FIXME: for trivial cases we can just return a clone of
ds->source? */
- return casereader_create_sequential (NULL,
- dict_get_next_value_idx (ds->dict),
- CASENUMBER_MAX,
- &proc_casereader_class, ds);
+
+ /* Create casereader and insert a shim on top. The shim allows us to
+ arbitrarily extend the casereader's lifetime, by slurping the cases into
+ the shim's buffer in proc_commit(). That is especially useful when output
+ table_items are generated directly from the procedure casereader (e.g. by
+ the LIST procedure) when we are using an output driver that keeps a
+ reference to the output items passed to it (e.g. the GUI output driver in
+ PSPPIRE). */
+ reader = casereader_create_sequential (NULL, dict_get_proto (ds->dict),
+ CASENUMBER_MAX,
+ &proc_casereader_class, ds);
+ ds->shim = casereader_shim_insert (reader);
+ 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
c = casereader_read (ds->source);
if (c == NULL)
return NULL;
- c = case_unshare_and_resize (c, dict_get_next_value_idx (ds->dict));
+ c = case_unshare_and_resize (c, dict_get_proto (ds->dict));
caseinit_init_vars (ds->caseinit, c);
/* Execute permanent transformations. */
struct dataset *ds = ds_;
struct ccase *c;
+ /* We are always the subreader for a casereader_buffer, so if we're being
+ destroyed then it's because the casereader_buffer has read all the cases
+ that it ever will. */
+ ds->shim = NULL;
+
/* Make sure transformations happen for every input case, in
case they have side effects, and ensure that the replacement
active file gets all the cases it should. */
bool
proc_commit (struct dataset *ds)
{
+ if (ds->shim != NULL)
+ casereader_shim_slurp (ds->shim);
+
assert (ds->proc_state == PROC_CLOSED);
ds->proc_state = PROC_COMMITTED;
else
{
const struct taint *taint = casereader_get_taint (ds->source);
- taint_reset_successor_taint ((struct taint *) taint);
+ taint_reset_successor_taint (CONST_CAST (struct taint *, taint));
assert (!taint_has_tainted_successor (taint));
}
}