File virtual_object.c¶
File List > cubrid > src > object > virtual_object.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* virtual_object.c - Transaction object handler for virtual objects
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "authenticate.h"
#include "virtual_object.h"
#include "set_object.h"
#include "object_accessor.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "db.h"
#include "schema_manager.h"
#include "view_transform.h"
#include "transaction_cl.h"
#include "dbtype.h"
#define ENCODED_LEN(siz) (2*(siz))
#define MAX_STRING_OID_LENGTH 4096
#define MIN_STRING_OID_LENGTH 18
const bool vid_inhibit_null_check = false;
static int vid_build_non_upd_object (MOP mop, DB_VALUE * seq);
static int vid_make_vid (OID * view_id, OID * proxy_id, DB_VALUE * val, DB_VALUE * vobj);
static int vid_db_value_size (DB_VALUE * dbval);
static char *vid_pack_db_value (char *lbuf, DB_VALUE * dbval);
static int vid_pack_vobj (char *buf, OID * view, OID * proxy, DB_VALUE * keys, int *vobj_size, int buflen);
static int vid_get_class_object (MOP class_p, SM_CLASS ** class_object_p);
static int vid_is_new_oobj (MOP mop);
#if defined(ENABLE_UNUSED_FUNCTION)
static int vid_convert_object_attr_value (SM_ATTRIBUTE * attribute_p, DB_VALUE * source_value,
DB_VALUE * destination_value, int *has_object);
#endif
/*
* vid_get_class_object() - get class object given its class mop
* return: NO_ERROR if all OK, a negative error code otherwise
* class_p(in): a class mop
* obj(out): class_p' class object if all OK, NULL otherwise
*
* Note:
* modifies: er state, obj
* effects : set obj to class_p' class object
*/
static int
vid_get_class_object (MOP class_p, SM_CLASS ** class_object_p)
{
if (!class_p || !class_object_p)
{
return ER_GENERIC_ERROR;
}
if (ws_find (class_p, (MOBJ *) class_object_p) == WS_FIND_MOP_DELETED || !(*class_object_p))
{
const OID *oid = NULL;
if (class_p->is_vid)
{
oid = &oid_Null_oid;
}
else
{
oid = &class_p->oid_info.oid;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_CLASS_OF_INSTANCE, 3, oid->volid, oid->pageid,
oid->slotid);
return ER_HEAP_UNKNOWN_CLASS_OF_INSTANCE;
}
return NO_ERROR;
}
/*
* vid_is_new_oobj() - is this a new OO instance that's about to be created?
* return: true iff mop is a new OO instance that's about to be created
* mop(in): a proxy object instance
*
* Note:
* effects : returns true iff mop is a new (embryonic) OO proxy object
*/
static int
vid_is_new_oobj (MOP mop)
{
SM_CLASS *class_p;
int return_code = 0;
return_code = (mop && mop->is_vid && ws_find (ws_class_mop (mop), (MOBJ *) (&class_p)) != WS_FIND_MOP_DELETED
&& (mop->oid_info.vid_info->flags & VID_NEW));
return return_code;
}
/*
* vid_is_new_pobj() - is this a new proxy instance that's about to be created?
* return: true iff mop is a new proxy instance that's about to be created
* mop(in): a proxy object instance
*
* Note:
* effects : returns true iff mop is a new (embryonic) proxy object
*/
int
vid_is_new_pobj (MOP mop)
{
int return_code = 0;
return_code = (mop && mop->is_vid && (mop->oid_info.vid_info->flags & VID_NEW));
return return_code;
}
/*
* vid_make_vobj() - constructor for vobj type
* return: int
* view_oid(in): DB_VALUE oid of projected view or null oid
* class_oid(in): DB_VALUE oid of real class or null oid
* keys(in): DB_VALUE oid keys
* vobj(in): DB_VALUE pointer for vobj
*/
int
vid_make_vobj (const OID * view_oid, const OID * class_oid, const DB_VALUE * keys, DB_VALUE * vobj)
{
int error;
DB_VALUE oid_value;
DB_SEQ *seq;
seq = set_create_sequence (3);
if (seq == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
db_make_oid (&oid_value, (OID *) view_oid);
error = set_put_element (seq, 0, &oid_value);
if (error < 0)
{
return error;
}
db_make_oid (&oid_value, (OID *) class_oid);
error = set_put_element (seq, 1, &oid_value);
if (error < 0)
{
return error;
}
error = set_put_element (seq, 2, (DB_VALUE *) keys);
if (error < 0)
{
return error;
}
db_make_sequence (vobj, seq);
db_value_alter_type (vobj, DB_TYPE_VOBJ);
return NO_ERROR;
}
/*
* vid_fetch_instance() - FETCH A VIRTUAL INSTANCE
* return: MOBJ
* mop(in): Memory Object Pointer of virtual instance to fetch
* purpose(in): Fetch purpose: Valid ones:
* DB_FETCH_READ
* DB_FETCH_WRITE
* DB_FETCH_DIRTY
* read_fetch_version_type(in): Fetch version type in case of read
*
* Note:
* Fetch the instance associated with the given mop for the given purpose.
* Locks are handled by the underlying database.
*/
MOBJ
vid_fetch_instance (MOP mop, DB_FETCH_MODE purpose, LC_FETCH_VERSION_TYPE read_fetch_version_type)
{
MOBJ inst;
MOP class_mop, base_mop;
SM_CLASS *class_p;
LC_FETCH_VERSION_TYPE fetch_version_type;
if (!mop->is_vid)
{
return (MOBJ) 0;
}
class_mop = ws_class_mop (mop);
if (vid_get_class_object (class_mop, &class_p) < 0)
{
return (MOBJ) 0;
}
ws_find (mop, &inst);
/* check for out-of-date instance */
if (inst && (mop->lock == NULL_LOCK))
{
ws_decache (mop);
inst = (MOBJ) 0;
}
if (!inst)
{
/* fetch the object pointed to by the vclass */
base_mop = vid_get_referenced_mop (mop);
if (base_mop)
{
AU_FETCHMODE fetch_mode;
if (purpose == DB_FETCH_WRITE)
{
fetch_mode = AU_FETCH_WRITE;
fetch_version_type = LC_FETCH_MVCC_VERSION;
}
else
{
fetch_mode = AU_FETCH_READ;
fetch_version_type = read_fetch_version_type;
}
if (au_fetch_instance_force (base_mop, &inst, fetch_mode, fetch_version_type) != NO_ERROR)
{
inst = (MOBJ) 0;
}
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_VID_LOST_NON_UPDATABLE_OBJECT, 0);
}
}
if ((purpose == DB_FETCH_WRITE) && (inst))
{
ws_dirty (mop);
}
return inst;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* vid_convert_object_attr_value() - Convert a object attribute value.
* return:
* attribute_p(in): attribute
* source_value(in): source value
* destination_value(in): destination value
* has_obj(out): nonzero iff source_value is (or has) a DB_TYPE_OBJECT (element)
* disguised into destination_value as a string value
*
* Note:
* Convert appropriate values from DB_TYPE_STRING to DB_TYPE_OBJECT &
* from DB_TYPE_OBJECT to DB_TYPE_STRING.
*/
static int
vid_convert_object_attr_value (SM_ATTRIBUTE * attribute_p, DB_VALUE * source_value, DB_VALUE * destination_value,
int *has_object)
{
int error = NO_ERROR;
VID_INFO *ref_vid_info;
DB_SET *set;
DB_SET *new_set;
int set_size;
int set_index;
DB_VALUE set_value;
DB_VALUE new_set_value;
MOP ref_mop, proxy_class_mop;
DB_OBJECT *temp_object = NULL;
*has_object = 0; /* assume no disguised objects until proven otherwise */
switch (DB_VALUE_TYPE (source_value))
{
case DB_TYPE_OBJECT:
{
db_value_domain_init (destination_value, DB_TYPE_STRING, DB_DEFAULT_PRECISION, 0);
temp_object = db_get_object (source_value);
if (temp_object != NULL)
{
ref_vid_info = temp_object->oid_info.vid_info;
if (ref_vid_info)
{
pr_clone_value (&ref_vid_info->keys, destination_value);
*has_object = 1; /* yes, it's an object disguised as a string */
}
}
}
break;
case DB_TYPE_VOBJ:
{
DB_TYPE type_id;
db_make_null (destination_value);
if (attribute_p->domain == NULL)
{
return error;
}
type_id = TP_DOMAIN_TYPE (attribute_p->domain);
if (type_id == DB_TYPE_OBJECT || TP_IS_SET_TYPE (type_id))
{
DB_VALUE setval;
set = db_get_set (source_value);
(void) db_seq_get (set, 1, &setval);
proxy_class_mop = db_get_object (&setval);
if (proxy_class_mop == NULL)
{
return error;
}
(void) db_seq_get (set, 2, &setval);
ref_mop = ws_vmop (proxy_class_mop, VID_UPDATABLE | VID_BASE, &setval);
if (ref_mop)
{
db_make_object (destination_value, ref_mop);
}
pr_clear_value (&setval);
}
}
break;
case DB_TYPE_SET:
case DB_TYPE_MULTISET:
{
set = db_get_set (source_value);
new_set = db_get_set (destination_value);
set_size = db_set_cardinality (set);
for (set_index = 0; set_index < set_size; ++set_index)
{
error = db_set_get (set, set_index, &set_value);
if (error != NO_ERROR)
{
continue;
}
error = vid_convert_object_attr_value (attribute_p, &set_value, &new_set_value, has_object);
if (error == NO_ERROR)
{
error = db_set_add (new_set, &new_set_value);
pr_clear_value (&new_set_value);
}
}
}
break;
case DB_TYPE_SEQUENCE:
{
set = db_get_set (source_value);
new_set = db_get_set (destination_value);
set_size = db_seq_size (set);
for (set_index = 0; set_index < set_size; ++set_index)
{
error = db_seq_get (set, set_index, &set_value);
if (error != NO_ERROR)
{
continue;
}
error = vid_convert_object_attr_value (attribute_p, &set_value, &new_set_value, has_object);
if (error == NO_ERROR)
{
error = db_seq_put (new_set, set_index, &new_set_value);
pr_clear_value (&new_set_value);
}
}
}
break;
default:
pr_clone_value (source_value, destination_value);
break;
}
return error;
}
#endif
/*
* vid_upd_instance() - PREPARE A VIRTUAL INSTANCE FOR UPDATE
* return: MOBJ
* mop(in): Mop of object that it is going to be updated
*
* Note:
* Prepare an instance for update. The instance is fetched for exclusive
* mode and it is set dirty. Note that it is very important the
* the instance is set dirty before it is actually updated, otherwise,
* the workspace may remain with a corrupted instance if a failure happens
*
* This function should be called before the instance is actually updated.
*/
MOBJ
vid_upd_instance (MOP mop)
{
MOBJ object; /* The instance object */
if (vid_is_new_oobj (mop))
{
/*
* don't fetch new (embryonic) OO instances because they
* are not there yet. we are buffering OO inserts.
*/
ws_find (mop, &object);
}
else
{
object = vid_fetch_instance (mop, DB_FETCH_WRITE, LC_FETCH_MVCC_VERSION);
/* The base instance is marked dirty by vid_fetch_instance */
}
return object;
}
/*
* vid_flush_all_instances() - Flush all dirty instances of the class
* associated with the given class_mop
* to the driver
* return: NO_ERROR or ER_FAILED
* class_mop(in): The class mop of the instances to flush
*
* decache(in): True, if instances must be decached after they are flushed
*
* Note:
* Flush all dirty instances of the class associated with the
* given class_mop to the driver
*/
int
vid_flush_all_instances (MOP class_mop, bool decache)
{
int rc;
if (ws_map_class (class_mop, vid_flush_instance, (void *) &decache) == WS_MAP_SUCCESS)
{
rc = NO_ERROR;
}
else
{
rc = ER_FAILED;
}
return rc;
}
/*
* vid_flush_and_rehash() - flush and rehash one proxy object instance
* return: NO_ERROR if all OK, an ER code otherwise
* mop(in): a dirty proxy object to be flushed
*
* Note:
* requires: mop points to a dirty proxy object to be flushed and rehashed
* modifies: mop
* effects : flush the object associated with the given mop and rehash it
* into its (possibly new) workspace hashtable address.
*/
int
vid_flush_and_rehash (MOP mop)
{
int return_code, isvid, isnew_oo;
bool isbase;
SM_CLASS *class_p;
isvid = mop->is_vid;
isbase = vid_is_base_instance (mop);
isnew_oo = vid_is_new_oobj (mop);
/* flush the proxy instance */
return_code = vid_flush_instance (mop, NULL);
if (return_code == WS_MAP_FAIL)
{
/* make sure we return an error */
assert (er_errid () != NO_ERROR);
return_code = er_errid ();
if (return_code >= NO_ERROR)
{
return_code = ER_GENERIC_ERROR;
}
ws_decache (mop);
}
else if (isvid && isbase && !isnew_oo)
{
/*
* rehash relational proxy mop into its new ws hashtable address.
* we must not rehash a new OO proxy mop because vid_flush_instance
* rehashes a new OO proxy mop (please see vid_store_oid_instance).
*/
return_code = vid_get_class_object (WS_CLASS_MOP (mop), &class_p);
if (return_code == NO_ERROR && !ws_rehash_vmop (mop, (MOBJ) class_p, NULL))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_WS_REHASH_VMOP_ERROR, 0);
return_code = ER_WS_REHASH_VMOP_ERROR;
}
}
return return_code;
}
/*
* vid_flush_and_rehash_lbl() - flush and rehash a proxy object instance label
*
* return: val if all OK, NULL otherwise
* val(in): an interpreter parameter value container
*
* Note:
* requires: val is an interpreter parameter value container that may
* point to a dirty proxy object to be flushed and rehashed
* modifies: val's dirty proxy object
* effects : if val holds anything other than a new dirty proxy object then
* simply return val. Otherwise, flush and rehash val's new dirty
* proxy object and return val.
*/
DB_VALUE *
vid_flush_and_rehash_lbl (DB_VALUE * value)
{
DB_OBJECT *mop;
if (!value)
{
return value;
}
if (DB_VALUE_TYPE (value) != DB_TYPE_OBJECT)
{
return value;
}
mop = db_get_object (value);
/* if val has anything other than a new dirty proxy object then do nothing */
if (mop == NULL || !vid_is_new_pobj (mop))
{
return value;
}
/* flush and rehash the new proxy object instance */
if (vid_flush_and_rehash (mop) != NO_ERROR)
{
return NULL; /* an error */
}
return value; /* all OK */
}
/*
* vid_allflush() - flush all dirty vclass objects
* return: NO_ERROR or ER_FAILED
*
* Note:
* modifies: all dirty vclass objects
* effects : flush all dirty vclass objects
*/
int
vid_allflush (void)
{
DB_OBJLIST *cl;
bool return_code;
int isvirt;
/*
* traverse the resident class list and
* for each proxy/vclass that has dirty instances
* call vid_flush_all to flush those dirty instances
*/
for (cl = ws_Resident_classes, return_code = NO_ERROR; cl != NULL && return_code == NO_ERROR; cl = cl->next)
{
if (ws_has_dirty_objects (cl->op, &isvirt) && isvirt)
{
return_code = vid_flush_all_instances (cl->op, DONT_DECACHE);
}
}
return return_code;
}
/*
* vid_add_virtual_instance() - INSERT A VIRTUAL OBJECT VIRTUAL INSTANCE
*
* return: MOP
* instance(in): Base instance object to add
* vclass_mop(in): Mop of vclass which will hold the instance
* bclass_mop(in):
* bclass(in):
*
* Note:
* Find one base class for the vclass indicated by class_mop.
* Make a MOP for the object for that base class. Then make a
* MOP for the vclass indicated by class_mop and set the keys
* to point to the base instance MOP.
*/
MOP
vid_add_virtual_instance (MOBJ instance, MOP vclass_mop, MOP bclass_mop, SM_CLASS * bclass)
{
MOP bmop; /* Mop of newly created base instance */
MOP vmop = NULL; /* Mop of newly created virtual instance */
if (!instance || !vclass_mop || !bclass_mop || !bclass)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_INVALID_ARGUMENTS, 0);
return NULL;
}
bmop = locator_add_instance (instance, bclass_mop);
if (bmop)
{
vmop = vid_build_virtual_mop (bmop, vclass_mop);
}
return vmop;
}
/*
* vid_build_virtual_mop() - create a virtual mop from a base class
* instance mop
* return: MOP
* bmop(in): Base instance mop to create virtual mop for
* vclass_mop(in): Mop of vclass which will hold the instance
*
*
* Note:
* Make a MOP for the vclass indicated by vclass_mop and set
* the keys to point to the base instance MOP.
*/
MOP
vid_build_virtual_mop (MOP bmop, MOP vclass_mop)
{
MOP vmop = NULL; /* Mop of newly created virtual instance */
DB_VALUE key; /* The key for the mop */
int vclass_updatable; /* Whether the vclass is updatable */
if (!bmop || !vclass_mop)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_INVALID_ARGUMENTS, 0);
return NULL;
}
vclass_updatable = mq_is_updatable (vclass_mop);
db_make_object (&key, bmop);
vmop = ws_vmop (vclass_mop, VID_NEW | vclass_updatable ? VID_UPDATABLE : 0, &key);
if (!vmop)
{
return NULL;
}
ws_set_lock (vmop, X_LOCK);
return vmop;
}
/*
* vid_get_referenced_mop() - Uses the VID to find the base class referenced
* by a vclass.
* return: MOP
* mop(in): The MOP for the vclass instance
*/
MOP
vid_get_referenced_mop (MOP mop)
{
VID_INFO *mop_vid_info;
if (!mop->is_vid)
{
goto end;
}
mop_vid_info = mop->oid_info.vid_info;
if (!mop_vid_info)
{
goto end;
}
if (DB_VALUE_TYPE (&mop_vid_info->keys) == DB_TYPE_OBJECT)
{
return db_get_object (&mop_vid_info->keys);
}
end:
return (MOP) 0;
}
/*
* vid_is_updatable() - Indicates whether the object is updatable.
* return: bool
* mop(in): The MOP for the vclass object
*/
bool
vid_is_updatable (MOP mop)
{
VID_INFO *mop_vid_info;
if (mop == NULL)
{
return false;
}
if (!mop->is_vid)
{
return false;
}
mop_vid_info = mop->oid_info.vid_info;
if (!mop_vid_info)
{
return false;
}
if (mop_vid_info->flags & VID_UPDATABLE)
{
return true;
}
return false;
}
/*
* vid_is_base_instance() - Indicates whether the given object is
* a base instance.
*
* return: bool
* mop(in): The MOP for the vclass object
*/
bool
vid_is_base_instance (MOP mop)
{
VID_INFO *mop_vid_info;
if (!mop->is_vid)
{
return false;
}
mop_vid_info = mop->oid_info.vid_info;
if (!mop_vid_info)
{
return false;
}
if (mop_vid_info->flags & VID_BASE)
{
return true;
}
return false;
}
/*
* vid_base_instance() - Returns the object or tuple instance for which
* the given instance of a virtual class.
*
* return: DB_OBJECT
* mop(in): Virtual class instance
*/
MOP
vid_base_instance (MOP mop)
{
if (vid_is_base_instance (mop))
{
return mop;
}
if (vid_is_updatable (mop))
{
return vid_get_referenced_mop (mop);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_OBJECT_NOT_UPDATABLE, 0);
return (DB_OBJECT *) 0;
}
}
/*
* vid_att_in_obj_id() - Indicates whether the attribute is in the object_id.
* return: bool
* att(in): Attribute
*/
bool
vid_att_in_obj_id (SM_ATTRIBUTE * attribute_p)
{
if (attribute_p->flags & SM_ATTFLAG_VID)
{
return true;
}
else
{
return false;
}
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* vid_set_att_obj_id() - Sets the object_id position for the attribute.
* return: int
* class_name(in):
* att(in): Attribute
* id_no(in): Attribute position in object_id
*/
int
vid_set_att_obj_id (const char *class_name, SM_ATTRIBUTE * attribute_p, int id_no)
{
int error = NO_ERROR;
DB_VALUE value;
if (!attribute_p->properties)
{
attribute_p->properties = classobj_make_prop ();
if (attribute_p->properties == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
if (classobj_get_prop (attribute_p->properties, SM_PROPERTY_VID_KEY, &value) > 0)
{
error = ER_SM_OBJECT_ID_ALREADY_SET;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, class_name);
return error;
}
attribute_p->flags |= SM_ATTFLAG_VID;
db_make_int (&value, id_no);
classobj_put_prop (attribute_p->properties, SM_PROPERTY_VID_KEY, &value);
return error;
}
/*
* vid_record_update() - Update the object to the driver as required.
* return: int
* mop(in): Memory Object Pointer for updated object
* class(in):
* att(in): Attribute that was updated
*/
int
vid_record_update (MOP mop, SM_CLASS * class_p, SM_ATTRIBUTE * attribute_p)
{
VID_INFO *mop_vid_info;
if (!mop->is_vid)
{
return NO_ERROR;
}
mop_vid_info = mop->oid_info.vid_info;
if (!mop_vid_info)
{
return NO_ERROR;
}
if (!(mop_vid_info->flags & VID_BASE))
{
return NO_ERROR;
}
ws_dirty (mop);
if (!vid_att_in_obj_id (attribute_p))
{
return NO_ERROR;
}
if (vid_flush_instance (mop, NULL) != WS_MAP_CONTINUE)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (!ws_rehash_vmop (mop, (MOBJ) class_p, NULL))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
return NO_ERROR;
}
#endif
/*
* vid_compare_non_updatable_objects() - Compare the values for the two objects
* If they are all equal, the objects
* are equal.
* return: non-zero if the objects are equal.
* mop1(in): MOP for a non-updatable object
* mop2(in): MOP for a non-updatable object
*/
bool
vid_compare_non_updatable_objects (MOP mop1, MOP mop2)
{
int error = NO_ERROR;
SM_CLASS *class1, *class2;
MOBJ inst1, inst2;
SM_ATTRIBUTE *att1, *att2;
char *mem1, *mem2;
SETREF *set1, *set2;
MOP attobj1, attobj2;
DB_VALUE val1, val2;
int rc;
if ((mop1 == NULL) || (mop2 == NULL))
{
error = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return false;
}
error = au_fetch_class_force (mop1, &class1, AU_FETCH_READ);
if (error != NO_ERROR)
{
error = ER_WS_NO_CLASS_FOR_INSTANCE;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
return false;
}
error = au_fetch_class_force (mop2, &class2, AU_FETCH_READ);
if (error != NO_ERROR)
{
error = ER_WS_NO_CLASS_FOR_INSTANCE;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
return false;
}
inst1 = (MOBJ) mop1->object;
inst2 = (MOBJ) mop2->object;
if ((!inst1) || (!inst2))
{
return false;
}
for (att1 = class1->attributes, att2 = class2->attributes; att1 != NULL && att2 != NULL;
att1 = (SM_ATTRIBUTE *) att1->header.next, att2 = (SM_ATTRIBUTE *) att2->header.next)
{
if (att1->type != att2->type)
{
return false;
}
mem1 = inst1 + att1->offset;
mem2 = inst2 + att2->offset;
if (pr_is_set_type (att1->type->id))
{
db_value_domain_init (&val1, att1->type->id, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
db_value_domain_init (&val2, att1->type->id, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
att1->type->getmem (mem1, att1->domain, &val1);
att2->type->getmem (mem2, att2->domain, &val2);
set1 = db_get_set (&val1);
set2 = db_get_set (&val2);
db_value_put_null (&val1);
db_value_put_null (&val2);
if ((set1 != NULL) && (set2 != NULL))
{
if (set_compare (set1, set2, 0) != DB_EQ)
{
return false;
}
}
else
{
return false;
}
}
else if (att1->type == tp_Type_object)
{
db_value_domain_init (&val1, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
db_value_domain_init (&val2, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
att1->type->getmem (mem1, att1->domain, &val1);
att2->type->getmem (mem2, att2->domain, &val2);
attobj1 = db_get_object (&val1);
attobj2 = db_get_object (&val2);
db_value_put_null (&val1);
db_value_put_null (&val2);
if (attobj1 != NULL && WS_IS_DELETED (attobj1))
{
attobj1 = NULL;
}
if (attobj2 != NULL && WS_IS_DELETED (attobj2))
{
attobj2 = NULL;
}
if (!attobj1 || (attobj1 != attobj2))
{
return false;
}
}
else
{
db_value_domain_init (&val1, att1->type->id, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
db_value_domain_init (&val2, att2->type->id, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
att1->type->getmem (mem1, att1->domain, &val1);
att2->type->getmem (mem2, att2->domain, &val2);
/*
* Unlike most calls to this function, don't perform coercion
* here so that an exact match can be performed. Note, this
* formerly called pr_value_equal which did kind of "halfway"
* coercion on the numeric types. Make sure the non-coercion
* behavior of tp_value_equal is appropriate for this use.
*/
rc = tp_value_equal (&val1, &val2, 0);
db_value_put_null (&val1);
db_value_put_null (&val2);
if (!rc)
{
return false;
}
}
}
if (att1 != NULL || att2 != NULL)
{
return false;
}
return true;
}
/*
* vid_rem_instance() - Delete an instance. The instance is marked as deleted
* in the workspace.
* return: none
* mop(in): Memory Object pointer of object to remove
*/
void
vid_rem_instance (MOP mop)
{
MOP base_mop;
if (!mop->is_vid)
{
return;
}
if (!vid_is_updatable (mop))
{
return;
}
if (vid_is_base_instance (mop))
{
ws_mark_deleted (mop);
}
else
{
base_mop = vid_get_referenced_mop (mop);
if (base_mop)
{
ws_mark_deleted (base_mop);
}
}
}
/*
* vid_build_non_upd_object() - Builds an object for a MOP based on a sequence.
* The object is not updatable.
* return: int
* mop(in): Memory Object pointer of object to build
* seq(in): Sequence containing the non-updatable values
*/
static int
vid_build_non_upd_object (MOP mop, DB_VALUE * seq)
{
int error = NO_ERROR;
SM_CLASS *class_p;
MOBJ inst;
SM_ATTRIBUTE *attribute_p;
char *mem;
DB_VALUE val;
DB_COLLECTION *col;
DB_OBJECT *vmop;
LOCK lock;
if ((mop == NULL) || (seq == NULL))
{
error = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
error = au_fetch_class_force (mop, &class_p, AU_FETCH_READ);
if (error != NO_ERROR)
{
error = ER_WS_NO_CLASS_FOR_INSTANCE;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
if (class_p->class_type != SM_VCLASS_CT)
{
error = ER_SM_INVALID_CLASS;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
inst = obj_alloc (class_p, 0);
if (inst == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
return error;
}
/* free the previous object, if any */
if (mop->object)
{
#if defined(CUBRID_DEBUG)
/* check to see if anyone has this mop pinned */
if (mop->pinned)
{
/*
* this is a logical error, we can't free the object since
* someone has it pinned. It will leak.
*/
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_WS_PIN_VIOLATION, 0);
er_log_debug (ARG_FILE_LINE, "** SYSTEM ERROR ** crs_vobj_to_vmop() is overwriting a pinned object");
}
#endif /* CUBRID_DEBUG */
if (!mop->pinned)
{
obj_free_memory ((SM_CLASS *) ws_class_mop (mop)->object, (MOBJ) mop->object);
}
}
mop->object = inst;
col = db_get_set (seq);
for (attribute_p = class_p->attributes; attribute_p != NULL; attribute_p = (SM_ATTRIBUTE *) attribute_p->header.next)
{
error = db_seq_get (col, attribute_p->order, &val);
if (error < 0)
break;
mem = inst + attribute_p->offset;
switch (DB_VALUE_TYPE (&val))
{
case DB_TYPE_VOBJ:
{
error = vid_vobj_to_object (&val, &vmop);
if (!(error < 0))
{
db_value_domain_init (&val, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
db_make_object (&val, vmop);
}
}
break;
case DB_TYPE_SET:
case DB_TYPE_MULTISET:
case DB_TYPE_SEQUENCE:
{
error = set_convert_oids_to_objects (db_get_set (&val));
}
break;
default:
break;
}
if (error < 0)
break;
obj_assign_value (mop, attribute_p, mem, &val);
pr_clear_value (&val);
}
if (error < 0)
{
db_ws_free (inst);
mop->object = NULL;
return error;
}
/*
* lock it to avoid getting a Workspace pin violation
* later in vid_fetch_instance.
*/
lock = ws_get_lock (mop);
assert (lock >= NULL_LOCK);
lock = lock_conv (S_LOCK, lock);
ws_set_lock (mop, lock);
return error;
}
/*
* vid_decache_instance()
* return: none
* mop(in): Memory Object pointer
*/
void
vid_decache_instance (MOP mop)
{
if (!mop->is_vid)
{
return;
}
if ((vid_is_base_instance (mop)) || (!vid_is_updatable (mop)))
{
ws_set_lock (mop, NULL_LOCK);
ws_decache (mop);
}
} /* vid_decache_instance */
/*
* vid_get_keys() - Returns in val a "peek" at the keys contained in the vmop.
*
* return: none
* mop(in): Memory Object pointer
*
* val(in): return DB_VALUE for the vmop keys
*
* Note:
* This is to avoid gratuitous alloc/free of the values
* that would be done by db_value_clone. The caller MUST
* call db_value_clone if a copy of keys is needed. The
* caller should NOT clear the returned result.
*
* If given a non-virtual mop, this still returns the key
* feild which would be in that position (3rd) of a
* DB_TYPE_VOBJ. Consequently, any DB_TYPE_VOBJ can
* be contructed from a DB_OBJECT * (mop) by calling this
* function for the keys portion.
*/
void
vid_get_keys (MOP mop, DB_VALUE * value)
{
VID_INFO *mop_vid_info;
OID *oid;
if (mop == NULL)
{
return;
}
if (!mop->is_vid)
{
oid = ws_oid (mop);
db_make_oid (value, oid);
return;
}
mop_vid_info = mop->oid_info.vid_info;
if (mop_vid_info)
{
*value = mop_vid_info->keys;
value->need_clear = false;
return;
}
return;
}
/*
* vid_getall_mops() - fetch/lock all instances of a given proxy
* return: DB_OBLIST of class_p' instances if all OK, NULL otherwise
* class_mop(in): proxy/vclass class memory object pointer
* class_p(in): proxy/vclass class object
* purpose(in): DB_FETCH_READ or DB_FETCH_WRITE
*
*
* Note:
* Fetch/lock all instances (mops) of a given proxy/vclass.
* The list of mops is returned to the caller.
*/
DB_OBJLIST *
vid_getall_mops (MOP class_mop, SM_CLASS * class_p, DB_FETCH_MODE purpose)
{
const char *class_name;
int error = NO_ERROR;
DB_OBJLIST *objlst, *new1;
DB_QUERY_RESULT *qres;
DB_QUERY_ERROR query_error;
char query[2000];
int t, tuple_cnt;
DB_VALUE value;
MOP mop;
SM_CLASS_TYPE class_type;
/* make sure we have reasonable arguments */
if (!class_mop || !class_p)
{
return NULL;
}
/* flush any dirty instances */
if (vid_flush_all_instances (class_mop, false) != NO_ERROR)
{
return NULL;
}
/* put together a query to get all instances of class_p */
class_type = sm_get_class_type (class_p);
class_name = db_get_class_name (class_mop);
snprintf (query, sizeof (query) - 1, "SELECT [%s] FROM [%s]", class_name, class_name);
/* run the query */
error = db_compile_and_execute_local (query, &qres, &query_error);
if (error != NO_ERROR)
{
return NULL;
}
/* how many instances */
tuple_cnt = db_query_tuple_count (qres);
if (tuple_cnt == 0)
{
return NULL;
}
/* start with an empty objlist */
objlst = NULL;
/* install instances into this workspace */
for (t = 0; t < tuple_cnt; ++t)
{
/* advance to next row */
error = db_query_next_tuple (qres);
/* get instance mop */
if (error == DB_CURSOR_SUCCESS)
{
error = db_query_get_tuple_value_by_name (qres, (char *) class_name, &value);
}
/* allocate objlist node */
new1 = ml_ext_alloc_link ();
if (error != NO_ERROR || new1 == NULL)
{
ml_ext_free (objlst);
return NULL;
}
/* save instance mop into objlist */
new1->op = mop = db_get_object (&value);
new1->next = objlst;
objlst = new1;
}
/* recycle query results */
error = db_query_end (qres);
/* convert purpose into a lock */
switch (purpose)
{
case DB_FETCH_QUERY_WRITE:
case DB_FETCH_CLREAD_INSTWRITE:
/*
* we're forced to revert these back to DB_FETCH_WRITE because the
* proxy locking code downstream recognizes only DB_FETCH_WRITE when
* requesting xlocks, all other purpose values are treated as slock
* requests.
*/
purpose = DB_FETCH_WRITE;
break;
default:
break;
}
/* if XLOCKs were requested, get them now */
if (purpose == DB_FETCH_WRITE && db_fetch_list (objlst, purpose, 0) != NO_ERROR)
{
ml_ext_free (objlst);
return NULL;
}
return objlst;
}
/*
* vid_vobj_to_object() -
* return: NO_ERROR if all OK, a negative ER code otherwise.
* vobj(in): a DB_TYPE_VOBJ db_value
* mop(out): object instance installed into workspace for vobj
*
* Note:
* requires: vobj is a DB_TYPE_VOBJ db_value {view,proxy,keys}
* modifies: ws table, mop
* effects : lookup vobj in the workspace table
* if not found then add vobj's object instance into ws table
*/
int
vid_vobj_to_object (const DB_VALUE * vobj, DB_OBJECT ** mop)
{
DB_SEQ *seq;
int i, size, flags = 0;
DB_VALUE elem_value, keys;
DB_OBJECT *vclass = NULL, *bclass = NULL, *obj = NULL;
int error = NO_ERROR;
MOBJ inst;
/* make sure we have a good input argument */
if (!vobj || !mop || DB_VALUE_TYPE (vobj) != DB_TYPE_VOBJ || (seq = db_get_set (vobj)) == NULL
|| (size = db_set_size (seq)) != 3)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DL_ESYS, 1, "virtual object inconsistent");
return ER_DL_ESYS;
}
keys.domain.general_info.is_null = 1;
keys.need_clear = false;
*mop = NULL;
/*
* get vobjs components into: {vclass,bclass,keys}.
* a proxy instance will have a null vclass.
* a virtual class instance may have a null bclass.
*/
for (i = 0; i < size; i++)
{
db_set_get (seq, i, &elem_value);
switch (i)
{
case 0:
if (elem_value.domain.general_info.is_null != 0)
{
vclass = NULL;
}
else if (elem_value.domain.general_info.type == DB_TYPE_OBJECT)
{
vclass = db_get_object (&elem_value);
/*
* we need to guarantee that if this vclass
* exists and it's not yet in the workspace, we must fetch
* it in. Otherwise, db_decode_object() can fail.
*/
if (vclass && vclass->object == NULL
&& au_fetch_instance_force (vclass, &inst, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION ()))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DL_ESYS, 1, "view class inconsistent");
return ER_DL_ESYS;
}
break;
case 1:
if (elem_value.domain.general_info.is_null != 0)
{
bclass = NULL;
}
else if (elem_value.domain.general_info.type == DB_TYPE_OBJECT)
{
bclass = db_get_object (&elem_value);
if (bclass && bclass->object == NULL
&& au_fetch_instance_force (bclass, &inst, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION ()))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DL_ESYS, 1, "base class inconsistent");
return ER_DL_ESYS;
}
break;
case 2:
if (elem_value.domain.general_info.is_null == 0)
{
keys = elem_value; /* structure copy */
}
break;
default:
continue;
}
}
if (keys.domain.general_info.is_null == 0)
{
/*
* does it have a class/proxy component specified?
* This would mean a real, updatable object.
*/
if (bclass)
{
/* look it up or install it in the workspace */
flags = VID_UPDATABLE | VID_BASE;
obj = ws_vmop (bclass, flags, &keys);
}
else if (keys.domain.general_info.type == DB_TYPE_OBJECT)
{
/*
* The vclass refers to a class, but we left
* the class oid feild empty in the vobj.
*/
obj = db_get_object (&keys);
}
else if (keys.domain.general_info.type == DB_TYPE_VOBJ)
{
assert (false);
vid_vobj_to_object (&keys, &obj);
}
else
{
obj = NULL;
}
/* does it have a vclass component? */
if (!vclass)
{
/*
* with no view, then the result is the object
* we just calculated.
*/
*mop = obj;
}
else
{
DB_VALUE temp;
if (obj)
{
/* a view on an updatable class or proxy */
db_make_object (&temp, obj);
flags = VID_UPDATABLE;
*mop = ws_vmop (vclass, flags, &temp);
}
else
{
if (keys.domain.general_info.type == DB_TYPE_SEQUENCE)
{
/*
* The vclass refers to a non-updatable view result.
* look it up or install it in the workspace.
*/
flags = 0;
*mop = ws_vmop (vclass, flags, &keys);
if (*mop)
{
error = vid_build_non_upd_object (*mop, &keys);
}
}
}
}
if (!*mop)
{
if (error == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DL_ESYS, 1, "vobject error");
error = ER_DL_ESYS;
}
}
}
pr_clear_value (&keys);
return error;
}
/*
* vid_oid_to_object() - turn an OID into an OBJECT type db_value
* return: NO_ERROR if all OK, an er code otherwise
* val(in/out): a db_value
* mop(in):
*
* Note:
* modifies: val
* effects : if val is an OID or has an OID turn it into an OBJECT value
*/
int
vid_oid_to_object (const DB_VALUE * value, DB_OBJECT ** mop)
{
OID *oid;
*mop = NULL;
/* make sure we have a reasonable argument */
if (!value)
{
return ER_GENERIC_ERROR;
}
/* OIDs must be turned into objects */
switch (DB_VALUE_TYPE (value))
{
case DB_TYPE_OID:
oid = (OID *) db_get_oid (value);
if (oid != NULL && !OID_ISNULL (oid))
{
*mop = ws_mop (oid, NULL);
}
break;
case DB_TYPE_SET:
case DB_TYPE_MULTISET:
case DB_TYPE_SEQUENCE:
return set_convert_oids_to_objects (db_get_set (value));
default:
break;
}
return NO_ERROR;
}
/*
* vid_object_to_vobj() -
* return: NO_ERROR if all OK, a negative ER code otherwise
* obj(in): a virtual object instance in the workspace
* vobj(out): a DB_TYPE_VOBJ db_value
*
* Note:
* requires: obj is a virtual mop in the workspace
* modifies: vobj, set/seq memory pool
* effects : builds the DB_TYPE_VOBJ db_value form of the given virtual mop
*/
int
vid_object_to_vobj (const DB_OBJECT * obj, DB_VALUE * vobj)
{
/* convert a workspace DB_TYPE_OBJECT to a DB_TYPE_VOBJ */
const OID *view_oid;
const OID *class_oid;
DB_VALUE keys;
DB_OBJECT *view_class;
DB_OBJECT *real_class;
DB_OBJECT *real_object;
(vobj)->domain.general_info.is_null = 1;
(vobj)->need_clear = false;
if (!obj)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DL_ESYS, 1, "null virtual object");
return ER_DL_ESYS;
}
class_oid = &oid_Null_oid;
view_oid = &oid_Null_oid;
view_class = db_get_class ((DB_OBJECT *) obj);
real_object = db_real_instance ((DB_OBJECT *) obj);
real_class = db_get_class (real_object);
if (view_class)
{
view_oid = ws_oid (view_class);
}
if (real_class)
{
class_oid = ws_oid (real_class);
}
if (real_object)
{
vid_get_keys (real_object, &keys);
}
else
{
vid_get_keys ((DB_OBJECT *) obj, &keys);
}
return vid_make_vobj (view_oid, class_oid, &keys, vobj);
}
/*
* vid_make_vid() - convert db_value into a virtual id
* return: return NO_ERROR if all OK, ER_FAILED otherwise
* view_id(in): vclass oid or NULL
* proxy_id(in): proxy oid or NULL
* val(in): a db_value (vobj's keys)
* vobj(in): the encoded VOBJ
*
* Note:
* requires: Caller must clear the created VOBJ db_value
* modifies: vobj is filled with the triple: view_id, proxy_id, keys
* effects : create the VOBJ encoding.
*/
static int
vid_make_vid (OID * view_id, OID * proxy_id, DB_VALUE * val, DB_VALUE * vobj)
{
int has_proxy, has_view;
DB_VALUE tval;
DB_SEQ *seq;
has_proxy = (proxy_id && !OID_ISNULL (proxy_id));
has_view = (view_id && !OID_ISNULL (view_id));
seq = db_seq_create (NULL, NULL, 3);
if (seq == NULL)
{
return ER_FAILED;
}
if (has_view)
{
db_make_oid (&tval, view_id);
}
else
{
db_value_domain_init (&tval, DB_TYPE_OID, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
OID_SET_NULL (&tval.data.oid);
}
if (db_seq_put (seq, 0, &tval) != NO_ERROR)
{
return ER_FAILED;
}
if (has_proxy)
{
db_make_oid (&tval, proxy_id);
}
else
{
db_value_domain_init (&tval, DB_TYPE_OID, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
OID_SET_NULL (&tval.data.oid);
}
if (db_seq_put (seq, 1, &tval) != NO_ERROR)
{
return ER_FAILED;
}
if (db_seq_put (seq, 2, val) != NO_ERROR)
{
return ER_FAILED;
}
db_make_sequence (vobj, seq);
db_value_alter_type (vobj, DB_TYPE_VOBJ);
return NO_ERROR;
}
/*
* vid_db_value_size() - size a db_value
* return: packed size of db_value
* dbval(in): dbvalue to size
*/
static int
vid_db_value_size (DB_VALUE * dbval)
{
int val_size;
val_size = pr_data_writeval_disk_size (dbval);
/*
* some OR_PUT functions assume data to be copied is always properly aligned,
* so we oblige here at the cost of maybe some extra space
*/
val_size = DB_ALIGN (val_size, MAX_ALIGNMENT);
return val_size;
}
/*
* vid_pack_db_value() - pack a db_value
* return: new buf position
* lbuf(in): the listfile destination buffer
* dbval(in): dbvalue to pack
*
* Note:
* requires: buf must have enough space for packed dbval
* DB_IS_NULL(dbval) == false
* modifies: buf
* effects : pack val in its listfile form into buf
*/
static char *
vid_pack_db_value (char *lbuf, DB_VALUE * dbval)
{
OR_BUF buf;
const PR_TYPE *pr_type;
int val_size;
DB_TYPE dbval_type;
dbval_type = DB_VALUE_DOMAIN_TYPE (dbval);
if (dbval_type > DB_TYPE_LAST || dbval_type == DB_TYPE_TABLE)
{
return NULL;
}
pr_type = tp_Type_id_map[(int) dbval_type];
val_size = pr_data_writeval_disk_size (dbval);
or_init (&buf, lbuf, val_size);
if (pr_type->data_writeval (&buf, dbval) != NO_ERROR)
{
return NULL;
}
lbuf += val_size;
return lbuf;
}
/*
* vid_pack_vobj() -
* return: int
* buf(in):
* view(in):
* proxy(in):
* keys(in):
* vobj_size(in):
* buflen(in):
*/
static int
vid_pack_vobj (char *buf, OID * view, OID * proxy, DB_VALUE * keys, int *vobj_size, int buflen)
{
DB_VALUE vobj;
if (vid_make_vid (view, proxy, keys, &vobj) != NO_ERROR)
{
return ER_FAILED;
}
*vobj_size = vid_db_value_size (&vobj);
if (buf)
{
/*
* vobj_size contains alignment bytes. When we encode this vobj during
* packing, we need to have known values in the alignment bytes else
* we can not reconstruct this vobj. Since we don't know the number of
* alignment bytes, we'll zero the buffer. (Always use a canon to kill
* a cat :-). This is probably okay since we're usually talking about
* some number of bytes less than 100. -- dkh
*/
if (buflen <= *vobj_size)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_ENCODE_VOBJ, 0);
return ER_FAILED;
}
(void) memset (buf, 0, *vobj_size);
buf = vid_pack_db_value (buf, &vobj);
}
db_value_clear (&vobj);
return NO_ERROR;
}
/*
* vid_encode_object() -
* return: int
* object(in):
* string(in):
* allocated_length(in):
* actual_length(in):
*/
int
vid_encode_object (DB_OBJECT * object, char *string, int allocated_length, int *actual_length)
{
DB_OBJECT *class_;
OID *temp_oid;
int is_class = 0;
if (object == NULL || (class_ = db_get_class (object)) == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, 0, 0, 0);
return (ER_HEAP_UNKNOWN_OBJECT);
}
if (string == NULL || (allocated_length < MIN_STRING_OID_LENGTH))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_BUFFER_TOO_SMALL, 0);
return ER_OBJ_BUFFER_TOO_SMALL;
}
/*
* classify the object into one of:
* - instance of a class
* - instance of a proxy
* - instance of a vclass of a class
* - instance of a vclass of a proxy
* - nonupdatable object
*/
class_ = db_get_class (object);
if (class_ == NULL)
{
return ER_FAILED;
}
is_class = db_is_any_class (object);
if (is_class < 0)
{
return is_class;
}
if (!is_class)
{
is_class = db_is_class (class_);
if (is_class < 0)
{
return is_class;
}
}
if (is_class)
{
/* if the specified string length is less than */
/* MIN_STRING_OID_LENGTH, we return this actual length */
if (er_errid () == ER_OBJ_BUFFER_TOO_SMALL)
{
*actual_length = MIN_STRING_OID_LENGTH;
return ER_OBJ_BUFFER_TOO_SMALL;
}
/* it's an instance of a local class */
string[0] = DB_INSTANCE_OF_A_CLASS;
/* make sure its oid is a good one */
temp_oid = WS_OID (object);
if (OID_ISTEMP (temp_oid))
{
if (locator_assign_permanent_oid (object))
temp_oid = WS_OID (object);
else
{
if (er_errid () == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_ASSIGN_OID, 0);
}
return er_errid ();
}
}
/* encode oid into an ascii string */
or_encode (&string[1], (const char *) temp_oid, OR_OID_SIZE);
*actual_length = MIN_STRING_OID_LENGTH;
return NO_ERROR;
}
{
DB_OBJECT *real_object, *real_class;
int vobj_len, i;
OID *view, *proxy;
DB_VALUE *keys, obj_key, val;
DB_ATTRIBUTE *attrs;
DB_SEQ *seq;
char vobj_buf[MAX_STRING_OID_LENGTH];
if (db_is_updatable_object (object))
{
/* it's updatable. get its real instance */
real_object = db_real_instance (object);
if (!real_object || real_object == object)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_VOBJ_MAPS_INVALID_OBJ, 0);
return (ER_OBJ_VOBJ_MAPS_INVALID_OBJ);
}
/* get {view,proxy,keys} of this vclass instance */
view = WS_OID (class_);
real_class = db_get_class (real_object);
is_class = db_is_class (real_class);
if (is_class < 0)
{
return is_class;
}
else if (is_class > 0)
{
/*
* it's an instance of a vclass of a class
* represented in vobj form as {view,NULL,keys}
*/
string[0] = DB_INSTANCE_OF_A_VCLASS_OF_A_CLASS;
proxy = NULL;
/*
* since we're doing the reverse of crs_cp_vobj_to_dbvalue,
* we form keys as a DB_TYPE_OBJECT db_value containing real_obj.
* by forming keys this way, we let crs_cp_vobj_to_dbvalue yield
* object back, given the {view,NULL,keys} built here.
*/
keys = &obj_key;
db_make_object (keys, real_object);
/* fall through to vobj packing/encoding */
}
else
{
/*
* a vclass of a vclass should have been resolved into
* a vclass of a class or a proxy during compilation,
* therefore it's considered an error at run-time.
*/
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_RESOLVE_VOBJ_TO_OBJ, 0);
return (ER_OBJ_CANT_RESOLVE_VOBJ_TO_OBJ);
}
}
else
{ /* its a nonupdatable object */
string[0] = DB_INSTANCE_OF_NONUPDATABLE_OBJECT;
view = WS_OID (class_);
proxy = NULL;
keys = &obj_key;
/*
* object has values only. it doesn't have any keys. so we form
* object's values into a sequence of values to become vobj's keys
*/
seq = db_seq_create (NULL, NULL, 0);
db_make_sequence (keys, seq);
/*
* there may be a safe way to speed this up by getting
* the values directly and bypassing authorization checks
*/
for (attrs = db_get_attributes (object), i = 0; attrs; attrs = db_attribute_next (attrs), i++)
{
if (db_get (object, db_attribute_name (attrs), &val) < 0 || db_seq_put (seq, i, &val) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_ENCODE_NONUPD_OBJ, 0);
return (ER_OBJ_CANT_ENCODE_NONUPD_OBJ);
}
}
/* fall through to vobj packing/encoding */
}
/* pack {view,proxy,keys} in listfile form into vobj_buf */
/* pass vobj_buf's allocated length here to avoid a memory overrun */
if (vid_pack_vobj (vobj_buf, view, proxy, keys, &vobj_len, MAX_STRING_OID_LENGTH) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_ENCODE_VOBJ, 0);
return (ER_OBJ_CANT_ENCODE_VOBJ);
}
/* take care not to overrun str */
*actual_length = ENCODED_LEN (vobj_len + OR_INT_SIZE) + 1;
if (*actual_length > allocated_length)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_BUFFER_TOO_SMALL, 0);
return (ER_OBJ_BUFFER_TOO_SMALL);
}
/* stuff vobj_len+vobj into str */
or_encode (&string[1], (const char *) &vobj_len, OR_INT_SIZE);
or_encode (&string[1 + ENCODED_LEN (OR_INT_SIZE)], (const char *) vobj_buf, vobj_len);
return NO_ERROR;
}
}
/*
* vid_decode_object()
* return: int
* string(in):
* object(out):
*/
int
vid_decode_object (const char *string, DB_OBJECT ** object)
{
OID obj_id;
DB_VALUE val;
int vobj_len = 0, rc = NO_ERROR;
size_t len;
OR_BUF buf;
char vobj_buf[MAX_STRING_OID_LENGTH], *bufp = vobj_buf;
/* make sure we got reasonable arguments */
if (object == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NULL_ADDR_OUTPUT_OBJ, 0);
return (ER_OBJ_NULL_ADDR_OUTPUT_OBJ);
}
if (string == NULL || !strlen (string))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENT, 0);
return ER_OBJ_INVALID_ARGUMENT;
}
/* guard against overrunning vobj_buf */
len = strlen (string);
if (len >= MAX_STRING_OID_LENGTH && (bufp = (char *) malloc (len)) == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, len);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
switch (string[0])
{
case DB_INSTANCE_OF_A_CLASS:
/* decode rest of str into a MOP */
or_decode (&string[1], (char *) &obj_id, OR_OID_SIZE);
*object = ws_mop (&obj_id, NULL);
break;
case DB_INSTANCE_OF_A_VCLASS_OF_A_CLASS:
case DB_INSTANCE_OF_NONUPDATABLE_OBJECT:
/* decode vobj_len */
or_decode (&string[1], (char *) &vobj_len, OR_INT_SIZE);
/* decode vobj */
or_decode (&string[1 + ENCODED_LEN (OR_INT_SIZE)], bufp, vobj_len);
/* convert vobj into a db_object */
or_init (&buf, bufp, vobj_len);
if (cursor_copy_vobj_to_dbvalue (&buf, &val) != NO_ERROR)
{
*object = NULL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INTERNAL_ERROR_IN_DECODING, 0);
rc = ER_OBJ_INTERNAL_ERROR_IN_DECODING;
}
else
{
*object = db_get_object (&val);
}
break;
default:
*object = NULL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENT, 0);
rc = ER_OBJ_INVALID_ARGUMENT;
break;
}
if (!(*object) && rc == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_DELETED, 0);
rc = ER_OBJ_DELETED;
}
/* free any dynamically allocated buffer */
if (bufp != vobj_buf)
{
free_and_init (bufp);
}
return rc;
}
/*
* vid_flush_instance() - Update the object to the driver
* return: WS_MAP_CONTINUE if all OK, WS_MAP_FAIL otherwise
* mop(in): Memory Object pointer of object to flush
* arg(in): nonzero iff caller wants mop decached after the flush
*/
int
vid_flush_instance (MOP mop, void *arg)
{
int rc = WS_MAP_CONTINUE, isvid, isdirty;
bool isupdatable, isbase;
VID_INFO *mop_vid_info;
isvid = mop->is_vid;
isdirty = mop->dirty;
isupdatable = vid_is_updatable (mop);
isbase = vid_is_base_instance (mop);
if (!mop->is_vid)
{
return rc;
}
if (isvid && isdirty)
{
if (isupdatable && isbase)
{
ws_clean (mop);
if (mop->is_vid)
{
mop_vid_info = mop->oid_info.vid_info;
if (mop_vid_info)
{
mop_vid_info->flags &= ~VID_NEW;
}
}
}
if (arg)
{
bool decache = *(bool *) arg;
if (decache)
{
ws_decache (mop);
}
}
}
return rc;
}