Commit 4410e38c authored by Jim Fulton's avatar Jim Fulton

Changed to make all persistent objects transactional.

parent adc281ba
/* /*
$Id: cPersistence.c,v 1.6 1997/03/20 20:58:25 jim Exp $ $Id: cPersistence.c,v 1.7 1997/03/25 20:43:21 jim Exp $
C Persistence Module C Persistence Module
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.6 1997/03/20 20:58:25 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.7 1997/03/25 20:43:21 jim Exp $";
#include <time.h> #include <time.h>
#include "cPersistence.h" #include "cPersistence.h"
...@@ -218,6 +218,64 @@ Per___changed__(self, args) ...@@ -218,6 +218,64 @@ Per___changed__(self, args)
return PyInt_FromLong(self->state == CHANGED_STATE); return PyInt_FromLong(self->state == CHANGED_STATE);
} }
static PyObject *
T___changed__(cPersistentObject *self, PyObject *args)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T;
if(PyArg_Parse(args, "O", &o))
{
int t;
t=PyObject_IsTrue(o);
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{
UNLESS(get_transaction)
{
UNLESS(builtins)
{
UNLESS(T=PyImport_ImportModule("__main__")) return NULL;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS(T) return NULL;
UNLESS(py_register=PyString_FromString("register")) goto err;
builtins=T;
}
UNLESS(get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if(get_transaction)
{
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL;
UNLESS_ASSIGN(T,PyObject_GetAttr(T,py_register)) return NULL;
UNLESS(o=PyTuple_New(1)) goto err;
Py_INCREF(self);
PyTuple_SET_ITEM(o,0,(PyObject*)self);
ASSIGN(o,PyObject_CallObject(T,o));
Py_DECREF(T);
UNLESS(o) return NULL;
Py_DECREF(o);
}
}
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None);
return Py_None;
}
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
}
err:
Py_DECREF(T);
return NULL;
}
static char Per___save____doc__[] = static char Per___save____doc__[] =
"__save__() -- Update the object in a persistent database." "__save__() -- Update the object in a persistent database."
; ;
...@@ -297,13 +355,7 @@ Per__p___reinit__(cPersistentObject *self, PyObject *args) ...@@ -297,13 +355,7 @@ Per__p___reinit__(cPersistentObject *self, PyObject *args)
if((self->oid==oid || PyObject_Compare(self->oid, oid)==0) if((self->oid==oid || PyObject_Compare(self->oid, oid)==0)
&& self->state==cPersistent_UPTODATE_STATE) && self->state==cPersistent_UPTODATE_STATE)
{ {
UNLESS(args=PyObject_GetAttr(copy,py___dict__)) UNLESS(args=PyObject_GetAttr(copy,py___dict__)) return NULL;
{
printf("Could not get __dict__ of a %s,\n", self->ob_type->tp_name);
PyObject_Print(self,stdout,0);
printf("\n\n");
return NULL;
}
ASSIGN(INSTANCE_DICT(self),args); ASSIGN(INSTANCE_DICT(self),args);
self->state=GHOST_STATE; self->state=GHOST_STATE;
} }
...@@ -425,7 +477,7 @@ err: ...@@ -425,7 +477,7 @@ err:
static struct PyMethodDef Per_methods[] = { static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)Per___changed__, 0, Per___changed____doc__}, {"__changed__", (PyCFunction)T___changed__, 0, Per___changed____doc__},
{"__save__", (PyCFunction)Per___save__, 0, Per___save____doc__}, {"__save__", (PyCFunction)Per___save__, 0, Per___save____doc__},
{"__inform_commit__", (PyCFunction)Per___save__, 0, Per___inform_commit____doc__}, {"__inform_commit__", (PyCFunction)Per___save__, 0, Per___inform_commit____doc__},
{"__inform_abort__", (PyCFunction)Per___inform_abort__, 0, Per___inform_abort____doc__}, {"__inform_abort__", (PyCFunction)Per___inform_abort__, 0, Per___inform_abort____doc__},
...@@ -448,8 +500,6 @@ Per_dealloc(self) ...@@ -448,8 +500,6 @@ Per_dealloc(self)
{ {
Py_XDECREF(self->oid); Py_XDECREF(self->oid);
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
Py_XDECREF(self->rtime);
Py_XDECREF(self->atime); Py_XDECREF(self->atime);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -533,17 +583,6 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name) ...@@ -533,17 +583,6 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name)
return Py_None; return Py_None;
} }
break; break;
case 'r':
if(strcmp(n,"ead_time")==0)
{
if(self->rtime)
{
Py_INCREF(self->rtime);
return self->rtime;
}
else
return PyFloat_FromDouble(0.0);
}
} }
} }
if(! (*name++=='_' && *name++=='_' && strcmp(name,"dict__")==0)) if(! (*name++=='_' && *name++=='_' && strcmp(name,"dict__")==0))
...@@ -615,12 +654,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v) ...@@ -615,12 +654,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
self->state=PyObject_IsTrue(v); self->state=PyObject_IsTrue(v);
return 0; return 0;
} }
if(strcmp(name+3,"read_time")==0)
{
ASSIGN(self->rtime, v);
Py_INCREF(self->rtime);
return 0;
}
} }
else else
{ {
...@@ -727,7 +760,7 @@ void ...@@ -727,7 +760,7 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.6 $"; char *rev="$Revision: 1.7 $";
PATimeType.ob_type=&PyType_Type; PATimeType.ob_type=&PyType_Type;
...@@ -754,6 +787,9 @@ initcPersistence() ...@@ -754,6 +787,9 @@ initcPersistence()
/**************************************************************************** /****************************************************************************
$Log: cPersistence.c,v $ $Log: cPersistence.c,v $
Revision 1.7 1997/03/25 20:43:21 jim
Changed to make all persistent objects transactional.
Revision 1.6 1997/03/20 20:58:25 jim Revision 1.6 1997/03/20 20:58:25 jim
Fixed bug in reinit. Fixed bug in reinit.
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: Persistence.py,v 1.4 1997/03/14 16:19:55 jim Exp $''' $Id: Persistence.py,v 1.5 1997/03/25 20:42:42 jim Exp $'''
# Copyright # Copyright
# #
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne # Copyright 1996 Digital Creations, L.C., 910 Princess Anne
...@@ -60,6 +60,9 @@ $Id: Persistence.py,v 1.4 1997/03/14 16:19:55 jim Exp $''' ...@@ -60,6 +60,9 @@ $Id: Persistence.py,v 1.4 1997/03/14 16:19:55 jim Exp $'''
# (540) 371-6909 # (540) 371-6909
# #
# $Log: Persistence.py,v $ # $Log: Persistence.py,v $
# Revision 1.5 1997/03/25 20:42:42 jim
# Changed to make all persistent objects transactional.
#
# Revision 1.4 1997/03/14 16:19:55 jim # Revision 1.4 1997/03/14 16:19:55 jim
# Changed so no longer save on del. # Changed so no longer save on del.
# Added check in __save__ so that we don't save if we have decided that # Added check in __save__ so that we don't save if we have decided that
...@@ -76,7 +79,7 @@ $Id: Persistence.py,v 1.4 1997/03/14 16:19:55 jim Exp $''' ...@@ -76,7 +79,7 @@ $Id: Persistence.py,v 1.4 1997/03/14 16:19:55 jim Exp $'''
# #
# #
# #
__version__='$Revision: 1.4 $'[11:-2] __version__='$Revision: 1.5 $'[11:-2]
class Persistent: class Persistent:
"""\ """\
...@@ -210,7 +213,32 @@ class Persistent: ...@@ -210,7 +213,32 @@ class Persistent:
else: else:
return self._p_changed return self._p_changed
try: from cPersistence import Persistent # The following was copied from the SingleThreadedTransaction module:
#
# Base class for all transactional objects
# Transactional objects, like persistent objects track
# changes in state. Unlike persistent objects, transactional
# objects work in conjunction with a transaction manager to manage
# saving state and recovering from errors.
#
def __changed__(self,v=None):
if v and not self._p_changed and self._p_jar is not None:
try: get_transaction().register(self)
except: pass
return Persistence.Persistent.__changed__(self,v)
def __inform_commit__(self,transaction,start_time):
self.__save__()
def __inform_abort__(self,transaction,start_time):
try: self._p_jar.oops(self,start_time)
except: pass
try:
import cPersistence
from cPersistence import Persistent
except: pass except: pass
class PersistentMapping(Persistent): class PersistentMapping(Persistent):
......
/* /*
$Id: cPersistence.c,v 1.6 1997/03/20 20:58:25 jim Exp $ $Id: cPersistence.c,v 1.7 1997/03/25 20:43:21 jim Exp $
C Persistence Module C Persistence Module
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.6 1997/03/20 20:58:25 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.7 1997/03/25 20:43:21 jim Exp $";
#include <time.h> #include <time.h>
#include "cPersistence.h" #include "cPersistence.h"
...@@ -218,6 +218,64 @@ Per___changed__(self, args) ...@@ -218,6 +218,64 @@ Per___changed__(self, args)
return PyInt_FromLong(self->state == CHANGED_STATE); return PyInt_FromLong(self->state == CHANGED_STATE);
} }
static PyObject *
T___changed__(cPersistentObject *self, PyObject *args)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T;
if(PyArg_Parse(args, "O", &o))
{
int t;
t=PyObject_IsTrue(o);
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{
UNLESS(get_transaction)
{
UNLESS(builtins)
{
UNLESS(T=PyImport_ImportModule("__main__")) return NULL;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS(T) return NULL;
UNLESS(py_register=PyString_FromString("register")) goto err;
builtins=T;
}
UNLESS(get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if(get_transaction)
{
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL;
UNLESS_ASSIGN(T,PyObject_GetAttr(T,py_register)) return NULL;
UNLESS(o=PyTuple_New(1)) goto err;
Py_INCREF(self);
PyTuple_SET_ITEM(o,0,(PyObject*)self);
ASSIGN(o,PyObject_CallObject(T,o));
Py_DECREF(T);
UNLESS(o) return NULL;
Py_DECREF(o);
}
}
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None);
return Py_None;
}
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
}
err:
Py_DECREF(T);
return NULL;
}
static char Per___save____doc__[] = static char Per___save____doc__[] =
"__save__() -- Update the object in a persistent database." "__save__() -- Update the object in a persistent database."
; ;
...@@ -297,13 +355,7 @@ Per__p___reinit__(cPersistentObject *self, PyObject *args) ...@@ -297,13 +355,7 @@ Per__p___reinit__(cPersistentObject *self, PyObject *args)
if((self->oid==oid || PyObject_Compare(self->oid, oid)==0) if((self->oid==oid || PyObject_Compare(self->oid, oid)==0)
&& self->state==cPersistent_UPTODATE_STATE) && self->state==cPersistent_UPTODATE_STATE)
{ {
UNLESS(args=PyObject_GetAttr(copy,py___dict__)) UNLESS(args=PyObject_GetAttr(copy,py___dict__)) return NULL;
{
printf("Could not get __dict__ of a %s,\n", self->ob_type->tp_name);
PyObject_Print(self,stdout,0);
printf("\n\n");
return NULL;
}
ASSIGN(INSTANCE_DICT(self),args); ASSIGN(INSTANCE_DICT(self),args);
self->state=GHOST_STATE; self->state=GHOST_STATE;
} }
...@@ -425,7 +477,7 @@ err: ...@@ -425,7 +477,7 @@ err:
static struct PyMethodDef Per_methods[] = { static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)Per___changed__, 0, Per___changed____doc__}, {"__changed__", (PyCFunction)T___changed__, 0, Per___changed____doc__},
{"__save__", (PyCFunction)Per___save__, 0, Per___save____doc__}, {"__save__", (PyCFunction)Per___save__, 0, Per___save____doc__},
{"__inform_commit__", (PyCFunction)Per___save__, 0, Per___inform_commit____doc__}, {"__inform_commit__", (PyCFunction)Per___save__, 0, Per___inform_commit____doc__},
{"__inform_abort__", (PyCFunction)Per___inform_abort__, 0, Per___inform_abort____doc__}, {"__inform_abort__", (PyCFunction)Per___inform_abort__, 0, Per___inform_abort____doc__},
...@@ -448,8 +500,6 @@ Per_dealloc(self) ...@@ -448,8 +500,6 @@ Per_dealloc(self)
{ {
Py_XDECREF(self->oid); Py_XDECREF(self->oid);
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
Py_XDECREF(self->rtime);
Py_XDECREF(self->atime); Py_XDECREF(self->atime);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -533,17 +583,6 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name) ...@@ -533,17 +583,6 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name)
return Py_None; return Py_None;
} }
break; break;
case 'r':
if(strcmp(n,"ead_time")==0)
{
if(self->rtime)
{
Py_INCREF(self->rtime);
return self->rtime;
}
else
return PyFloat_FromDouble(0.0);
}
} }
} }
if(! (*name++=='_' && *name++=='_' && strcmp(name,"dict__")==0)) if(! (*name++=='_' && *name++=='_' && strcmp(name,"dict__")==0))
...@@ -615,12 +654,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v) ...@@ -615,12 +654,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
self->state=PyObject_IsTrue(v); self->state=PyObject_IsTrue(v);
return 0; return 0;
} }
if(strcmp(name+3,"read_time")==0)
{
ASSIGN(self->rtime, v);
Py_INCREF(self->rtime);
return 0;
}
} }
else else
{ {
...@@ -727,7 +760,7 @@ void ...@@ -727,7 +760,7 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.6 $"; char *rev="$Revision: 1.7 $";
PATimeType.ob_type=&PyType_Type; PATimeType.ob_type=&PyType_Type;
...@@ -754,6 +787,9 @@ initcPersistence() ...@@ -754,6 +787,9 @@ initcPersistence()
/**************************************************************************** /****************************************************************************
$Log: cPersistence.c,v $ $Log: cPersistence.c,v $
Revision 1.7 1997/03/25 20:43:21 jim
Changed to make all persistent objects transactional.
Revision 1.6 1997/03/20 20:58:25 jim Revision 1.6 1997/03/20 20:58:25 jim
Fixed bug in reinit. Fixed bug in reinit.
......
/* /*
$Id: cPersistence.c,v 1.6 1997/03/20 20:58:25 jim Exp $ $Id: cPersistence.c,v 1.7 1997/03/25 20:43:21 jim Exp $
C Persistence Module C Persistence Module
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.6 1997/03/20 20:58:25 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.7 1997/03/25 20:43:21 jim Exp $";
#include <time.h> #include <time.h>
#include "cPersistence.h" #include "cPersistence.h"
...@@ -218,6 +218,64 @@ Per___changed__(self, args) ...@@ -218,6 +218,64 @@ Per___changed__(self, args)
return PyInt_FromLong(self->state == CHANGED_STATE); return PyInt_FromLong(self->state == CHANGED_STATE);
} }
static PyObject *
T___changed__(cPersistentObject *self, PyObject *args)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T;
if(PyArg_Parse(args, "O", &o))
{
int t;
t=PyObject_IsTrue(o);
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{
UNLESS(get_transaction)
{
UNLESS(builtins)
{
UNLESS(T=PyImport_ImportModule("__main__")) return NULL;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS(T) return NULL;
UNLESS(py_register=PyString_FromString("register")) goto err;
builtins=T;
}
UNLESS(get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if(get_transaction)
{
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL;
UNLESS_ASSIGN(T,PyObject_GetAttr(T,py_register)) return NULL;
UNLESS(o=PyTuple_New(1)) goto err;
Py_INCREF(self);
PyTuple_SET_ITEM(o,0,(PyObject*)self);
ASSIGN(o,PyObject_CallObject(T,o));
Py_DECREF(T);
UNLESS(o) return NULL;
Py_DECREF(o);
}
}
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None);
return Py_None;
}
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
}
err:
Py_DECREF(T);
return NULL;
}
static char Per___save____doc__[] = static char Per___save____doc__[] =
"__save__() -- Update the object in a persistent database." "__save__() -- Update the object in a persistent database."
; ;
...@@ -297,13 +355,7 @@ Per__p___reinit__(cPersistentObject *self, PyObject *args) ...@@ -297,13 +355,7 @@ Per__p___reinit__(cPersistentObject *self, PyObject *args)
if((self->oid==oid || PyObject_Compare(self->oid, oid)==0) if((self->oid==oid || PyObject_Compare(self->oid, oid)==0)
&& self->state==cPersistent_UPTODATE_STATE) && self->state==cPersistent_UPTODATE_STATE)
{ {
UNLESS(args=PyObject_GetAttr(copy,py___dict__)) UNLESS(args=PyObject_GetAttr(copy,py___dict__)) return NULL;
{
printf("Could not get __dict__ of a %s,\n", self->ob_type->tp_name);
PyObject_Print(self,stdout,0);
printf("\n\n");
return NULL;
}
ASSIGN(INSTANCE_DICT(self),args); ASSIGN(INSTANCE_DICT(self),args);
self->state=GHOST_STATE; self->state=GHOST_STATE;
} }
...@@ -425,7 +477,7 @@ err: ...@@ -425,7 +477,7 @@ err:
static struct PyMethodDef Per_methods[] = { static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)Per___changed__, 0, Per___changed____doc__}, {"__changed__", (PyCFunction)T___changed__, 0, Per___changed____doc__},
{"__save__", (PyCFunction)Per___save__, 0, Per___save____doc__}, {"__save__", (PyCFunction)Per___save__, 0, Per___save____doc__},
{"__inform_commit__", (PyCFunction)Per___save__, 0, Per___inform_commit____doc__}, {"__inform_commit__", (PyCFunction)Per___save__, 0, Per___inform_commit____doc__},
{"__inform_abort__", (PyCFunction)Per___inform_abort__, 0, Per___inform_abort____doc__}, {"__inform_abort__", (PyCFunction)Per___inform_abort__, 0, Per___inform_abort____doc__},
...@@ -448,8 +500,6 @@ Per_dealloc(self) ...@@ -448,8 +500,6 @@ Per_dealloc(self)
{ {
Py_XDECREF(self->oid); Py_XDECREF(self->oid);
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
Py_XDECREF(self->rtime);
Py_XDECREF(self->atime); Py_XDECREF(self->atime);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -533,17 +583,6 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name) ...@@ -533,17 +583,6 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name)
return Py_None; return Py_None;
} }
break; break;
case 'r':
if(strcmp(n,"ead_time")==0)
{
if(self->rtime)
{
Py_INCREF(self->rtime);
return self->rtime;
}
else
return PyFloat_FromDouble(0.0);
}
} }
} }
if(! (*name++=='_' && *name++=='_' && strcmp(name,"dict__")==0)) if(! (*name++=='_' && *name++=='_' && strcmp(name,"dict__")==0))
...@@ -615,12 +654,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v) ...@@ -615,12 +654,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
self->state=PyObject_IsTrue(v); self->state=PyObject_IsTrue(v);
return 0; return 0;
} }
if(strcmp(name+3,"read_time")==0)
{
ASSIGN(self->rtime, v);
Py_INCREF(self->rtime);
return 0;
}
} }
else else
{ {
...@@ -727,7 +760,7 @@ void ...@@ -727,7 +760,7 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.6 $"; char *rev="$Revision: 1.7 $";
PATimeType.ob_type=&PyType_Type; PATimeType.ob_type=&PyType_Type;
...@@ -754,6 +787,9 @@ initcPersistence() ...@@ -754,6 +787,9 @@ initcPersistence()
/**************************************************************************** /****************************************************************************
$Log: cPersistence.c,v $ $Log: cPersistence.c,v $
Revision 1.7 1997/03/25 20:43:21 jim
Changed to make all persistent objects transactional.
Revision 1.6 1997/03/20 20:58:25 jim Revision 1.6 1997/03/20 20:58:25 jim
Fixed bug in reinit. Fixed bug in reinit.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment