1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/dataset.h"
26 #include "data/case.h"
27 #include "data/case-map.h"
28 #include "data/caseinit.h"
29 #include "data/casereader.h"
30 #include "data/casereader-provider.h"
31 #include "data/casereader-shim.h"
32 #include "data/casewriter.h"
33 #include "data/dictionary.h"
34 #include "data/file-handle-def.h"
35 #include "data/session.h"
36 #include "data/transformations.h"
37 #include "data/variable.h"
38 #include "libpspp/deque.h"
39 #include "libpspp/misc.h"
40 #include "libpspp/str.h"
41 #include "libpspp/taint.h"
42 #include "libpspp/i18n.h"
44 #include "gl/minmax.h"
45 #include "gl/xalloc.h"
48 /* A dataset is usually part of a session. Within a session its name must
49 unique. The name must either be a valid PSPP identifier or the empty
50 string. (It must be unique within the session even if it is the empty
51 string; that is, there may only be a single dataset within a session with
52 the empty string as its name.) */
53 struct session *session;
55 enum dataset_display display;
57 /* Cases are read from source,
58 their transformation variables are initialized,
59 pass through permanent_trns_chain (which transforms them into
60 the format described by permanent_dict),
62 pass through temporary_trns_chain (which transforms them into
63 the format described by dict),
64 and are finally passed to the procedure. */
65 struct casereader *source;
66 struct caseinit *caseinit;
67 struct trns_chain permanent_trns_chain;
68 struct dictionary *permanent_dict;
69 struct casewriter *sink;
70 struct trns_chain temporary_trns_chain;
72 struct dictionary *dict;
74 /* Stack of transformation chains for DO IF and LOOP and INPUT PROGRAM. */
75 struct trns_chain *stack;
77 size_t allocated_stack;
79 /* If true, cases are discarded instead of being written to
83 /* The case map used to compact a case, if necessary;
84 otherwise a null pointer. */
85 struct case_map *compactor;
87 /* Time at which proc was last invoked. */
88 time_t last_proc_invocation;
90 /* Cases just before ("lagging") the current one. */
91 int n_lag; /* Number of cases to lag. */
92 struct deque lag; /* Deque of lagged cases. */
93 struct ccase **lag_cases; /* Lagged cases managed by deque. */
98 PROC_COMMITTED, /* No procedure in progress. */
99 PROC_OPEN, /* proc_open called, casereader still open. */
100 PROC_CLOSED /* casereader from proc_open destroyed,
101 but proc_commit not yet called. */
104 casenumber cases_written; /* Cases output so far. */
105 bool ok; /* Error status. */
106 struct casereader_shim *shim; /* Shim on proc_open() casereader. */
108 const struct dataset_callbacks *callbacks;
111 /* Uniquely distinguishes datasets. */
115 static void dataset_changed__ (struct dataset *);
116 static void dataset_transformations_changed__ (struct dataset *,
119 static void add_case_limit_trns (struct dataset *ds);
120 static void add_filter_trns (struct dataset *ds);
122 static void update_last_proc_invocation (struct dataset *ds);
125 dict_callback (struct dictionary *d UNUSED, void *ds_)
127 struct dataset *ds = ds_;
128 dataset_changed__ (ds);
132 dataset_create_finish__ (struct dataset *ds, struct session *session)
134 static unsigned int seqno;
136 dict_set_change_callback (ds->dict, dict_callback, ds);
137 proc_cancel_all_transformations (ds);
138 dataset_set_session (ds, session);
142 /* Creates a new dataset named NAME, adds it to SESSION, and returns it. If
143 SESSION already contains a dataset named NAME, it is deleted and replaced.
144 The dataset initially has an empty dictionary and no data source. */
146 dataset_create (struct session *session, const char *name)
148 struct dataset *ds = XMALLOC (struct dataset);
149 *ds = (struct dataset) {
150 .name = xstrdup (name),
151 .display = DATASET_FRONT,
152 .dict = dict_create (get_default_encoding ()),
153 .caseinit = caseinit_create (),
155 dataset_create_finish__ (ds, session);
160 /* Creates and returns a new dataset that has the same data and dictionary as
161 OLD named NAME, adds it to the same session as OLD, and returns the new
162 dataset. If SESSION already contains a dataset named NAME, it is deleted
165 OLD must not have any active transformations or temporary state and must
166 not be in the middle of a procedure.
168 Callbacks are not cloned. */
170 dataset_clone (struct dataset *old, const char *name)
174 assert (old->proc_state == PROC_COMMITTED);
175 assert (!old->permanent_trns_chain.n);
176 assert (old->permanent_dict == NULL);
177 assert (old->sink == NULL);
178 assert (!old->temporary);
179 assert (!old->temporary_trns_chain.n);
181 new = xzalloc (sizeof *new);
182 new->name = xstrdup (name);
183 new->display = DATASET_FRONT;
184 new->source = casereader_clone (old->source);
185 new->dict = dict_clone (old->dict);
186 new->caseinit = caseinit_clone (old->caseinit);
187 new->last_proc_invocation = old->last_proc_invocation;
190 dataset_create_finish__ (new, old->session);
197 dataset_destroy (struct dataset *ds)
201 dataset_set_session (ds, NULL);
203 dict_unref (ds->dict);
204 dict_unref (ds->permanent_dict);
205 caseinit_destroy (ds->caseinit);
206 trns_chain_uninit (&ds->permanent_trns_chain);
207 for (size_t i = 0; i < ds->n_stack; i++)
208 trns_chain_uninit (&ds->stack[i]);
210 dataset_transformations_changed__ (ds, false);
216 /* Discards the active dataset's dictionary, data, and transformations. */
218 dataset_clear (struct dataset *ds)
220 assert (ds->proc_state == PROC_COMMITTED);
222 dict_clear (ds->dict);
223 fh_set_default_handle (NULL);
227 casereader_destroy (ds->source);
230 proc_cancel_all_transformations (ds);
234 dataset_name (const struct dataset *ds)
240 dataset_set_name (struct dataset *ds, const char *name)
242 struct session *session = ds->session;
247 active = session_active_dataset (session) == ds;
249 session_set_active_dataset (session, NULL);
250 dataset_set_session (ds, NULL);
254 ds->name = xstrdup (name);
258 dataset_set_session (ds, session);
260 session_set_active_dataset (session, ds);
265 dataset_session (const struct dataset *ds)
271 dataset_set_session (struct dataset *ds, struct session *session)
273 if (session != ds->session)
275 if (ds->session != NULL)
276 session_remove_dataset (ds->session, ds);
278 session_add_dataset (session, ds);
282 /* Returns the dictionary within DS. This is always nonnull, although it
283 might not contain any variables. */
285 dataset_dict (const struct dataset *ds)
290 /* Replaces DS's dictionary by DICT, discarding any source and
293 dataset_set_dict (struct dataset *ds, struct dictionary *dict)
295 assert (ds->proc_state == PROC_COMMITTED);
296 assert (ds->dict != dict);
300 dict_unref (ds->dict);
302 dict_set_change_callback (ds->dict, dict_callback, ds);
305 /* Returns the casereader that will be read when a procedure is executed on
306 DS. This can be NULL if none has been set up yet. */
307 const struct casereader *
308 dataset_source (const struct dataset *ds)
313 /* Returns true if DS has a data source, false otherwise. */
315 dataset_has_source (const struct dataset *ds)
317 return dataset_source (ds) != NULL;
320 /* Replaces the active dataset's data by READER. READER's cases must have an
321 appropriate format for DS's dictionary. */
323 dataset_set_source (struct dataset *ds, struct casereader *reader)
325 casereader_destroy (ds->source);
328 caseinit_clear (ds->caseinit);
329 caseinit_mark_as_preinited (ds->caseinit, ds->dict);
331 return reader == NULL || !casereader_error (reader);
334 /* Returns the data source from DS and removes it from DS. Returns a null
335 pointer if DS has no data source. */
337 dataset_steal_source (struct dataset *ds)
339 struct casereader *reader = ds->source;
345 /* Returns a number unique to DS. It can be used to distinguish one dataset
346 from any other within a given program run, even datasets that do not exist
349 dataset_seqno (const struct dataset *ds)
355 dataset_set_callbacks (struct dataset *ds,
356 const struct dataset_callbacks *callbacks,
359 ds->callbacks = callbacks;
360 ds->cb_data = cb_data;
364 dataset_get_display (const struct dataset *ds)
370 dataset_set_display (struct dataset *ds, enum dataset_display display)
372 ds->display = display;
375 /* Returns the last time the data was read. */
377 time_of_last_procedure (struct dataset *ds)
381 if (ds->last_proc_invocation == 0)
382 update_last_proc_invocation (ds);
383 return ds->last_proc_invocation;
386 /* Regular procedure. */
388 /* Executes any pending transformations, if necessary.
389 This is not identical to the EXECUTE command in that it won't
390 always read the source data. This can be important when the
391 source data is given inline within BEGIN DATA...END FILE. */
393 proc_execute (struct dataset *ds)
397 if ((!ds->temporary || !ds->temporary_trns_chain.n)
398 && !ds->permanent_trns_chain.n)
401 ds->discard_output = false;
402 dict_set_case_limit (ds->dict, 0);
403 dict_clear_vectors (ds->dict);
407 ok = casereader_destroy (proc_open (ds));
408 return proc_commit (ds) && ok;
411 static const struct casereader_class proc_casereader_class;
413 /* Opens dataset DS for reading cases with proc_read. If FILTER is true, then
414 cases filtered out with FILTER BY will not be included in the casereader
415 (which is usually desirable). If FILTER is false, all cases will be
416 included regardless of FILTER BY settings.
418 proc_commit must be called when done. */
420 proc_open_filtering (struct dataset *ds, bool filter)
422 struct casereader *reader;
424 assert (ds->source != NULL);
425 assert (ds->proc_state == PROC_COMMITTED);
427 update_last_proc_invocation (ds);
429 caseinit_mark_for_init (ds->caseinit, ds->dict);
431 /* Finish up the collection of transformations. */
432 add_case_limit_trns (ds);
434 add_filter_trns (ds);
436 /* Make permanent_dict refer to the dictionary right before
437 data reaches the sink. */
438 if (ds->permanent_dict == NULL)
439 ds->permanent_dict = ds->dict;
442 if (!ds->discard_output)
444 struct dictionary *pd = ds->permanent_dict;
445 size_t compacted_n_values = dict_count_values (pd, 1u << DC_SCRATCH);
446 if (compacted_n_values < dict_get_next_value_idx (pd))
448 struct caseproto *compacted_proto;
449 compacted_proto = dict_get_compacted_proto (pd, 1u << DC_SCRATCH);
450 ds->compactor = case_map_to_compact_dict (pd, 1u << DC_SCRATCH);
451 ds->sink = autopaging_writer_create (compacted_proto);
452 caseproto_unref (compacted_proto);
456 ds->compactor = NULL;
457 ds->sink = autopaging_writer_create (dict_get_proto (pd));
462 ds->compactor = NULL;
466 /* Allocate memory for lagged cases. */
467 ds->lag_cases = deque_init (&ds->lag, ds->n_lag, sizeof *ds->lag_cases);
469 ds->proc_state = PROC_OPEN;
470 ds->cases_written = 0;
473 /* FIXME: use taint in dataset in place of `ok'? */
474 /* FIXME: for trivial cases we can just return a clone of
477 /* Create casereader and insert a shim on top. The shim allows us to
478 arbitrarily extend the casereader's lifetime, by slurping the cases into
479 the shim's buffer in proc_commit(). That is especially useful when output
480 table_items are generated directly from the procedure casereader (e.g. by
481 the LIST procedure) when we are using an output driver that keeps a
482 reference to the output items passed to it (e.g. the GUI output driver in
484 reader = casereader_create_sequential (NULL, dict_get_proto (ds->dict),
486 &proc_casereader_class, ds);
487 ds->shim = casereader_shim_insert (reader);
491 /* Opens dataset DS for reading cases with proc_read.
492 proc_commit must be called when done. */
494 proc_open (struct dataset *ds)
496 return proc_open_filtering (ds, true);
499 /* Returns true if a procedure is in progress, that is, if
500 proc_open has been called but proc_commit has not. */
502 proc_is_open (const struct dataset *ds)
504 return ds->proc_state != PROC_COMMITTED;
507 /* "read" function for procedure casereader. */
508 static struct ccase *
509 proc_casereader_read (struct casereader *reader UNUSED, void *ds_)
511 struct dataset *ds = ds_;
512 enum trns_result retval = TRNS_DROP_CASE;
515 assert (ds->proc_state == PROC_OPEN);
516 for (; ; case_unref (c))
518 assert (retval == TRNS_DROP_CASE || retval == TRNS_ERROR);
519 if (retval == TRNS_ERROR)
524 /* Read a case from source. */
525 c = casereader_read (ds->source);
528 c = case_unshare_and_resize (c, dict_get_proto (ds->dict));
529 caseinit_init_vars (ds->caseinit, c);
531 /* Execute permanent transformations. */
532 casenumber case_nr = ds->cases_written + 1;
533 retval = trns_chain_execute (&ds->permanent_trns_chain, case_nr, &c);
534 caseinit_update_left_vars (ds->caseinit, c);
535 if (retval != TRNS_CONTINUE)
538 /* Write case to collection of lagged cases. */
541 while (deque_count (&ds->lag) >= ds->n_lag)
542 case_unref (ds->lag_cases[deque_pop_back (&ds->lag)]);
543 ds->lag_cases[deque_push_front (&ds->lag)] = case_ref (c);
546 /* Write case to replacement dataset. */
548 if (ds->sink != NULL)
549 casewriter_write (ds->sink,
550 case_map_execute (ds->compactor, case_ref (c)));
552 /* Execute temporary transformations. */
553 if (ds->temporary_trns_chain.n)
555 retval = trns_chain_execute (&ds->temporary_trns_chain,
556 ds->cases_written, &c);
557 if (retval != TRNS_CONTINUE)
565 /* "destroy" function for procedure casereader. */
567 proc_casereader_destroy (struct casereader *reader, void *ds_)
569 struct dataset *ds = ds_;
572 /* We are always the subreader for a casereader_buffer, so if we're being
573 destroyed then it's because the casereader_buffer has read all the cases
574 that it ever will. */
577 /* Make sure transformations happen for every input case, in
578 case they have side effects, and ensure that the replacement
579 active dataset gets all the cases it should. */
580 while ((c = casereader_read (reader)) != NULL)
583 ds->proc_state = PROC_CLOSED;
584 ds->ok = casereader_destroy (ds->source) && ds->ok;
586 dataset_set_source (ds, NULL);
589 /* Must return false if the source casereader, a transformation,
590 or the sink casewriter signaled an error. (If a temporary
591 transformation signals an error, then the return value is
592 false, but the replacement active dataset may still be
595 proc_commit (struct dataset *ds)
597 if (ds->shim != NULL)
598 casereader_shim_slurp (ds->shim);
600 assert (ds->proc_state == PROC_CLOSED);
601 ds->proc_state = PROC_COMMITTED;
603 dataset_changed__ (ds);
605 /* Free memory for lagged cases. */
606 while (!deque_is_empty (&ds->lag))
607 case_unref (ds->lag_cases[deque_pop_back (&ds->lag)]);
608 free (ds->lag_cases);
610 /* Dictionary from before TEMPORARY becomes permanent. */
611 proc_cancel_temporary_transformations (ds);
613 if (!ds->discard_output)
615 /* Finish compacting. */
616 if (ds->compactor != NULL)
618 case_map_destroy (ds->compactor);
619 ds->compactor = NULL;
621 dict_delete_scratch_vars (ds->dict);
622 dict_compact_values (ds->dict);
625 /* Old data sink becomes new data source. */
626 if (ds->sink != NULL)
627 ds->source = casewriter_make_reader (ds->sink);
632 ds->discard_output = false;
636 caseinit_clear (ds->caseinit);
637 caseinit_mark_as_preinited (ds->caseinit, ds->dict);
639 dict_clear_vectors (ds->dict);
640 ds->permanent_dict = NULL;
641 return proc_cancel_all_transformations (ds) && ds->ok;
644 /* Casereader class for procedure execution. */
645 static const struct casereader_class proc_casereader_class =
647 proc_casereader_read,
648 proc_casereader_destroy,
653 /* Updates last_proc_invocation. */
655 update_last_proc_invocation (struct dataset *ds)
657 ds->last_proc_invocation = time (NULL);
660 /* Returns a pointer to the lagged case from N_BEFORE cases before the
661 current one, or NULL if there haven't been that many cases yet. */
663 lagged_case (const struct dataset *ds, int n_before)
665 assert (n_before >= 1);
666 assert (n_before <= ds->n_lag);
668 if (n_before <= deque_count (&ds->lag))
669 return ds->lag_cases[deque_front (&ds->lag, n_before - 1)];
674 /* Adds TRNS to the current set of transformations. */
676 add_transformation (struct dataset *ds,
677 const struct trns_class *class, void *aux)
679 struct trns_chain *chain = (ds->n_stack > 0 ? &ds->stack[ds->n_stack - 1]
680 : ds->temporary ? &ds->temporary_trns_chain
681 : &ds->permanent_trns_chain);
682 struct transformation t = { .class = class, .aux = aux };
683 trns_chain_append (chain, &t);
684 dataset_transformations_changed__ (ds, true);
687 /* Returns true if the next call to add_transformation() will add
688 a temporary transformation, false if it will add a permanent
691 proc_in_temporary_transformations (const struct dataset *ds)
693 return ds->temporary;
696 /* Marks the start of temporary transformations.
697 Further calls to add_transformation() will add temporary
700 proc_start_temporary_transformations (struct dataset *ds)
702 if (!proc_in_temporary_transformations (ds))
704 add_case_limit_trns (ds);
706 ds->permanent_dict = dict_clone (ds->dict);
708 ds->temporary = true;
709 dataset_transformations_changed__ (ds, true);
713 /* Converts all the temporary transformations, if any, to permanent
714 transformations. Further transformations will be permanent.
716 The FILTER command is implemented as a temporary transformation, so a
717 procedure that uses this function should usually use proc_open_filtering()
718 with FILTER false, instead of plain proc_open().
720 Returns true if anything changed, false otherwise. */
722 proc_make_temporary_transformations_permanent (struct dataset *ds)
724 if (proc_in_temporary_transformations (ds))
726 trns_chain_splice (&ds->permanent_trns_chain, &ds->temporary_trns_chain);
728 ds->temporary = false;
730 dict_unref (ds->permanent_dict);
731 ds->permanent_dict = NULL;
739 /* Cancels all temporary transformations, if any. Further
740 transformations will be permanent.
741 Returns true if anything changed, false otherwise. */
743 proc_cancel_temporary_transformations (struct dataset *ds)
745 if (proc_in_temporary_transformations (ds))
747 dict_unref (ds->dict);
748 ds->dict = ds->permanent_dict;
749 ds->permanent_dict = NULL;
751 trns_chain_clear (&ds->temporary_trns_chain);
753 dataset_transformations_changed__ (ds, ds->permanent_trns_chain.n != 0);
760 /* Cancels all transformations, if any.
761 Returns true if successful, false on I/O error. */
763 proc_cancel_all_transformations (struct dataset *ds)
766 assert (ds->proc_state == PROC_COMMITTED);
767 ok = trns_chain_clear (&ds->permanent_trns_chain);
768 ok = trns_chain_clear (&ds->temporary_trns_chain) && ok;
769 ds->temporary = false;
770 for (size_t i = 0; i < ds->n_stack; i++)
771 ok = trns_chain_uninit (&ds->stack[i]) && ok;
773 dataset_transformations_changed__ (ds, false);
779 proc_push_transformations (struct dataset *ds)
781 if (ds->n_stack >= ds->allocated_stack)
782 ds->stack = x2nrealloc (ds->stack, &ds->allocated_stack,
784 trns_chain_init (&ds->stack[ds->n_stack++]);
788 proc_pop_transformations (struct dataset *ds, struct trns_chain *chain)
790 assert (ds->n_stack > 0);
791 *chain = ds->stack[--ds->n_stack];
794 static enum trns_result
795 store_case_num (void *var_, struct ccase **cc, casenumber case_num)
797 struct variable *var = var_;
799 *cc = case_unshare (*cc);
800 *case_num_rw (*cc, var) = case_num;
802 return TRNS_CONTINUE;
805 /* Add a variable which we can sort by to get back the original order. */
807 add_permanent_ordering_transformation (struct dataset *ds)
809 struct variable *temp_var = dict_create_var_assert (ds->dict, "$ORDER", 0);
810 struct variable *order_var
811 = (proc_in_temporary_transformations (ds)
812 ? dict_clone_var_in_place_assert (ds->permanent_dict, temp_var)
815 static const struct trns_class trns_class = {
817 .execute = store_case_num
819 const struct transformation t = { .class = &trns_class, .aux = order_var };
820 trns_chain_append (&ds->permanent_trns_chain, &t);
825 /* Causes output from the next procedure to be discarded, instead
826 of being preserved for use as input for the next procedure. */
828 proc_discard_output (struct dataset *ds)
830 ds->discard_output = true;
834 /* Checks whether DS has a corrupted active dataset. If so,
835 discards it and returns false. If not, returns true without
838 dataset_end_of_command (struct dataset *ds)
840 if (ds->source != NULL)
842 if (casereader_error (ds->source))
849 const struct taint *taint = casereader_get_taint (ds->source);
850 taint_reset_successor_taint (CONST_CAST (struct taint *, taint));
851 assert (!taint_has_tainted_successor (taint));
857 /* Limits the maximum number of cases processed to
859 static enum trns_result
860 case_limit_trns_proc (void *cases_remaining_,
861 struct ccase **c UNUSED, casenumber case_nr UNUSED)
863 size_t *cases_remaining = cases_remaining_;
864 if (*cases_remaining > 0)
866 (*cases_remaining)--;
867 return TRNS_CONTINUE;
870 return TRNS_DROP_CASE;
873 /* Frees the data associated with a case limit transformation. */
875 case_limit_trns_free (void *cases_remaining_)
877 size_t *cases_remaining = cases_remaining_;
878 free (cases_remaining);
882 /* Adds a transformation that limits the number of cases that may
883 pass through, if DS->DICT has a case limit. */
885 add_case_limit_trns (struct dataset *ds)
887 casenumber case_limit = dict_get_case_limit (ds->dict);
890 casenumber *cases_remaining = xmalloc (sizeof *cases_remaining);
891 *cases_remaining = case_limit;
893 static const struct trns_class trns_class = {
894 .name = "case limit",
895 .execute = case_limit_trns_proc,
896 .destroy = case_limit_trns_free,
898 add_transformation (ds, &trns_class, cases_remaining);
900 dict_set_case_limit (ds->dict, 0);
905 /* FILTER transformation. */
906 static enum trns_result
907 filter_trns_proc (void *filter_var_,
908 struct ccase **c, casenumber case_nr UNUSED)
911 struct variable *filter_var = filter_var_;
912 double f = case_num (*c, filter_var);
913 return (f != 0.0 && !var_is_num_missing (filter_var, f, MV_ANY)
914 ? TRNS_CONTINUE : TRNS_DROP_CASE);
917 /* Adds a temporary transformation to filter data according to
918 the variable specified on FILTER, if any. */
920 add_filter_trns (struct dataset *ds)
922 struct variable *filter_var = dict_get_filter (ds->dict);
923 if (filter_var != NULL)
925 proc_start_temporary_transformations (ds);
927 static const struct trns_class trns_class = {
929 .execute = filter_trns_proc,
931 add_transformation (ds, &trns_class, filter_var);
936 dataset_need_lag (struct dataset *ds, int n_before)
938 ds->n_lag = MAX (ds->n_lag, n_before);
942 dataset_changed__ (struct dataset *ds)
944 if (ds->callbacks != NULL && ds->callbacks->changed != NULL)
945 ds->callbacks->changed (ds->cb_data);
949 dataset_transformations_changed__ (struct dataset *ds, bool non_empty)
951 if (ds->callbacks != NULL && ds->callbacks->transformations_changed != NULL)
952 ds->callbacks->transformations_changed (non_empty, ds->cb_data);
955 /* Private interface for use by session code. */
958 dataset_set_session__ (struct dataset *ds, struct session *session)
960 ds->session = session;