File object_accessor.c¶
File List > cubrid > src > object > object_accessor.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.
*
*/
/*
* object_accessor.c - Object accessor module.
*
* This contains code for attribute and method access, instance creation
* and deletion, and misc utilitities related to instances.
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include "chartype.h"
#include "error_manager.h"
#include "system_parameter.h"
#include "server_interface.h"
#include "dbtype.h"
#include "work_space.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "set_object.h"
#include "class_object.h"
#include "schema_manager.h"
#include "object_accessor.h"
#include "authenticate.h"
#include "db.h"
#include "locator_cl.h"
#include "virtual_object.h"
#include "parser.h"
#include "transaction_cl.h"
#include "trigger_manager.h"
#include "view_transform.h"
#include "network_interface_cl.h"
#include "dbtype.h"
/*
* OBJ_MAX_ARGS
*
* Note :
* This is the maximum number of arguments currently supported
* This should be unlimited when we have full support for overflow
* argument lists.
*
*/
#define OBJ_MAX_ARGS 32
#define MAX_DOMAIN_NAME 128
typedef enum
{
TEMPOID_FLUSH_FAIL = -1,
TEMPOID_FLUSH_OK = 0,
TEMPOID_FLUSH_NOT_SUPPORT = 1
} TEMPOID_FLUSH_RESULT;
/*
* argstate
*
* Note :
* This structure is built during the processing of a method call.
* It contains the cannonical internal representation of the method
* argument lists and other associated information.
* There are several functions that invoke methods, each with its own
* style of argument passing (stack, va_list, DB_VALUE_LIST, etc). All
* of these will translate their arguments into this form for further
* processing.
*
*/
typedef struct argstate
{
DB_VALUE *values[OBJ_MAX_ARGS];
DB_VALUE *save[OBJ_MAX_ARGS];
DB_VALUE_LIST *overflow;
DB_VALUE_LIST *save_overflow;
int nargs;
int noverflow;
int free_overflow;
} ARGSTATE;
char *obj_Method_error_msg;
static int forge_flag_pat = 0;
static int obj_Method_call_level = 0;
static MOP obj_find_object_by_cons_and_key (MOP classop, SM_CLASS_CONSTRAINT * cons, DB_VALUE * key,
AU_FETCHMODE fetchmode);
static int find_attribute (SM_CLASS ** classp, SM_ATTRIBUTE ** attp, MOP op, const char *name, int for_write);
static int find_shared_attribute (SM_CLASS ** classp, SM_ATTRIBUTE ** attp, MOP op, const char *name, int for_write);
static int assign_null_value (MOP op, SM_ATTRIBUTE * att, char *mem);
static int assign_set_value (MOP op, SM_ATTRIBUTE * att, char *mem, SETREF * setref);
static int obj_set_att (MOP op, SM_CLASS * class_, SM_ATTRIBUTE * att, DB_VALUE * value, SM_VALIDATION * valid);
static int get_object_value (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * source, DB_VALUE * dest);
static int get_set_value (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * source, DB_VALUE * dest);
static int obj_get_temp (DB_OBJECT * obj, SM_CLASS * class_, SM_ATTRIBUTE * att, DB_VALUE * value);
static int obj_set_temp (DB_OBJECT * obj, SM_ATTRIBUTE * att, DB_VALUE * value);
static void argstate_from_list (ARGSTATE * state, DB_VALUE_LIST * arglist);
static void argstate_from_array (ARGSTATE * state, DB_VALUE ** argarray);
static void argstate_from_va (ARGSTATE * state, va_list args, int nargs);
static void cleanup_argstate (ARGSTATE * state);
static int call_method (METHOD_FUNCTION method, MOP obj, DB_VALUE * returnval, int nargs, DB_VALUE ** values,
DB_VALUE_LIST * overow);
static int check_args (SM_METHOD * method, ARGSTATE * state);
static int obj_send_method_va (MOP obj, SM_CLASS * class_, SM_METHOD * method, DB_VALUE * returnval, va_list args);
static int obj_send_method_list (MOP obj, SM_CLASS * class_, SM_METHOD * method, DB_VALUE * returnval,
DB_VALUE_LIST * arglist);
static int obj_send_method_array (MOP obj, SM_CLASS * class_, SM_METHOD * method, DB_VALUE * returnval,
DB_VALUE ** argarray);
static MOP find_unique (MOP classop, SM_ATTRIBUTE * att, DB_VALUE * value, AU_FETCHMODE fetchmode);
static int flush_temporary_OID (MOP classop, DB_VALUE * key);
static DB_VALUE *obj_make_key_value (DB_VALUE * key, const DB_VALUE * values[], int size);
/* ATTRIBUTE LOCATION */
/*
* find_attribute - This is the primary attriubte lookup function
* for object operations.
* return: error code
* classp(out): class pointer (returned)
* attp(out): pointer to attribute descriptor (returned())
* op(in): class or object pointer
* name(in): attribute name
* for_write(in): flag set if intention is for update/alter
*
* Note:
* It will fetch the class with the proper mode and find the named
* attribute.
* Compare this with the new function sm_get_att_desc() and
* try to merge where possible.
*/
static int
find_attribute (SM_CLASS ** classp, SM_ATTRIBUTE ** attp, MOP op, const char *name, int for_write)
{
int error = NO_ERROR, is_class = 0;
SM_CLASS *class_;
SM_ATTRIBUTE *att;
DB_FETCH_MODE class_purpose;
bool find_class_attribute = false;
class_ = NULL;
att = NULL;
if (!op->is_temp)
{
class_purpose = ((for_write) ? DB_FETCH_WRITE : DB_FETCH_READ);
is_class = locator_is_class (op, class_purpose);
if (is_class < 0)
{
return is_class;
}
find_class_attribute = (is_class > 0 ? true : false);
}
if (find_class_attribute)
{
/* looking for class attribute */
if (for_write)
{
error = au_fetch_class (op, &class_, AU_FETCH_UPDATE, AU_ALTER);
}
else
{
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
}
if (error == NO_ERROR)
{
att = classobj_find_attribute (class_, name, 1);
}
}
else
{
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
if (error == NO_ERROR)
{
att = classobj_find_attribute (class_, name, 0);
if (att != NULL)
{
if (att->header.name_space == ID_SHARED_ATTRIBUTE)
{
/*
* sigh, we didn't know that this was going to be a shared attribute
* when we checked class authorization above, we must now upgrade
* the lock and check for alter access.
*
* Since this is logically in the name_space of the instance,
* should we use simple AU_UPDATE authorization rather than AU_ALTER
* even though we're technically modifying the class ?
*/
if (for_write)
{
error = au_fetch_class (op, &class_, AU_FETCH_UPDATE, AU_ALTER);
}
}
}
}
}
if (error == NO_ERROR && att == NULL)
{
ERROR1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
}
*classp = class_;
*attp = att;
return error;
}
/*
* find_shared_attribute - Used only to find a shared attribute given
* a class or instance.
* return: error
* classp(out): class pointer (returned)
* attp(out): pointer to pointer to attribute for shared attribute
* op(in): class or instance
* name(in): attribute name
* for_write(in):flag indicating that an update is intended
*
* Note:
* This is necessary because the behavior of find_attribute given a
* class object is to search for class attribute NOT shared attributes
* Because of this, when the user wants a shared attribute from a class
* object, they must specifically signal this intent by using the
* obj_get_shared (or obj_set_shared) interface functions.
*/
static int
find_shared_attribute (SM_CLASS ** classp, SM_ATTRIBUTE ** attp, MOP op, const char *name, int for_write)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_ATTRIBUTE *att;
att = NULL;
if (for_write)
{
error = au_fetch_class (op, &class_, AU_FETCH_UPDATE, AU_ALTER);
}
else
{
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
}
if (error == NO_ERROR)
{
if (for_write)
{
/* must call this when updating instances - is this necessary here ? */
ws_class_has_object_dependencies (ws_class_mop (op));
}
att = classobj_find_attribute (class_, name, 0);
if (att == NULL || att->header.name_space != ID_SHARED_ATTRIBUTE)
{
ERROR1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
}
}
*classp = class_;
*attp = att;
return error;
}
/*
* obj_locate_attribute -
* return: error code
* op(in): class or object pointer
* attid(in): id
* for_write(in):flag set if intention is for update/alter
* memp(out): pointer to instance memory block (returned)
* attp(out): pointer to attribute descriptor (returned)
*
* Note:
* This is an attribute lookup routine used when the attribute id
* is known. Since id ranges are unique across all attribute types,
* this can be used for normal, shared and class attributes.
* This is made public so that it can be used by the set module to
* locate set valued attributes for a set reference MOP.
* Similar to find_attribute() except that it also fetches the instance
* and returns the memory offset, consider merging the two.
*/
int
obj_locate_attribute (MOP op, int attid, int for_write, char **memp, SM_ATTRIBUTE ** attp)
{
int error = NO_ERROR, is_class = 0;
SM_CLASS *class_;
SM_ATTRIBUTE *att, *found;
MOBJ obj;
char *memory;
DB_FETCH_MODE class_purpose;
found = NULL;
memory = NULL;
/* need to handle this case */
if (op->is_temp)
{
ERROR0 (error, ER_OBJ_INVALID_TEMP_OBJECT);
return error;
}
class_purpose = ((for_write) ? DB_FETCH_WRITE : DB_FETCH_READ);
is_class = locator_is_class (op, class_purpose);
if (is_class < 0)
{
return is_class;
}
if (is_class)
{
if (for_write)
{
error = au_fetch_class (op, &class_, AU_FETCH_UPDATE, AU_ALTER);
}
else
{
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
}
if (error == NO_ERROR)
{
found = NULL;
for (att = class_->class_attributes; att != NULL && found == NULL; att = (SM_ATTRIBUTE *) att->header.next)
{
if (att->id == attid)
{
found = att;
}
}
}
}
else
{
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
if (error == NO_ERROR)
{
if (for_write)
{
error = au_fetch_instance (op, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_UPDATE);
}
else
{
error = au_fetch_instance (op, &obj, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION (), AU_SELECT);
}
if (error == NO_ERROR)
{
if (for_write)
{
/* must call this when updating instances */
ws_class_has_object_dependencies (ws_class_mop (op));
}
found = NULL;
for (att = class_->attributes; att != NULL && found == NULL; att = (SM_ATTRIBUTE *) att->header.next)
{
if (att->id == attid)
{
found = att;
}
}
if (found != NULL)
{
memory = (char *) (((char *) obj) + found->offset);
}
else
{
for (att = class_->shared; att != NULL && found == NULL; att = (SM_ATTRIBUTE *) att->header.next)
{
if (att->id == attid)
{
found = att;
}
}
if (found != NULL)
{
if (for_write)
{
error = au_fetch_class (op, &class_, AU_FETCH_UPDATE, AU_ALTER);
}
}
}
}
}
}
if (error == NO_ERROR && found == NULL)
{
ERROR1 (error, ER_OBJ_INVALID_ATTRIBUTE, "???");
}
if (attp != NULL)
{
*attp = found;
}
*memp = memory;
return error;
}
/* VALUE ASSIGNMENT */
/*
* assign_null_value - Work function for assign_value.
* return: error
* op(in): class or instance pointer
* att(in):attribute descriptor
* mem(in):pointer to instance memory (only if instance attribute)
*
* Note:
* This is used to set the value of an attribute to NULL.
*/
static int
assign_null_value (MOP op, SM_ATTRIBUTE * att, char *mem)
{
/*
* the mr_ functions are responsible for initializing/freeing the
* value if NULL is passed in
*/
MOBJ object = NULL;
if (mem == NULL)
{
pr_clear_value (&att->default_value.value);
}
else
{
if (att->domain->type->setmem (mem, att->domain, NULL))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
else
{
if (!att->domain->type->variable_p)
{
if ((ws_find (op, &object) == WS_FIND_MOP_DELETED) || object == NULL)
{
return ER_OBJ_INVALID_ARGUMENTS;
}
OBJ_CLEAR_BOUND_BIT (object, att->storage_order);
}
}
}
return NO_ERROR;
}
/*
* assign_set_value - Work function for assign_value
* return: error
* op(in): class or instance pointer
* att(in): attribute descriptor
* mem(in): pointer to instance memory (for instance attributes only)
* setref(in): set pointer to assign
*
* Note:
* This is used to assign a set value to an attribute. Sets have extra
* overhead in assignment to maintain the ownership information in the
* set descriptor. Unlike strings, sets are not freed when they are
* replaced in an assignment. They will be subject to gargabe collection.
*
* Make sure the set is checked for compliance with the attribute domain
* if the set currently has no domain specification.
*/
static int
assign_set_value (MOP op, SM_ATTRIBUTE * att, char *mem, SETREF * setref)
{
int error = NO_ERROR, is_class = 0;
MOP owner;
SETREF *new_set, *current_set;
DB_VALUE val;
/* change ownership of the set, copy if necessary */
if (setref == NULL)
{
new_set = NULL;
}
else
{
owner = op;
if (mem == NULL)
{
is_class = locator_is_class (op, DB_FETCH_WRITE);
if (is_class < 0)
{
return is_class;
}
if (!is_class)
{
owner = ws_class_mop (op);
}
}
new_set = set_change_owner (setref, owner, att->id, att->domain);
if (new_set == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
}
if (error == NO_ERROR)
{
/* assign the value */
if (mem != NULL)
{
switch (TP_DOMAIN_TYPE (att->domain))
{
case DB_TYPE_SET:
default:
db_make_set (&val, new_set);
break;
case DB_TYPE_MULTISET:
db_make_multiset (&val, new_set);
break;
case DB_TYPE_SEQUENCE:
db_make_sequence (&val, new_set);
break;
}
error = att->domain->type->setmem (mem, att->domain, &val);
db_value_put_null (&val);
if (error == NO_ERROR)
{
if (new_set != NULL && new_set != setref)
{
set_free (new_set);
}
}
}
else
{
/*
* remove ownership information in the current set,
* need to be able to free this !!!
*/
current_set = db_get_set (&att->default_value.value);
if (current_set != NULL)
{
error = set_disconnect (current_set);
}
if (error == NO_ERROR)
{
/* set the new value */
if (new_set != NULL)
{
switch (TP_DOMAIN_TYPE (att->domain))
{
case DB_TYPE_SET:
default:
db_make_set (&att->default_value.value, new_set);
break;
case DB_TYPE_MULTISET:
db_make_multiset (&att->default_value.value, new_set);
break;
case DB_TYPE_SEQUENCE:
db_make_sequence (&att->default_value.value, new_set);
break;
}
}
else
{
db_make_null (&att->default_value.value);
}
if (new_set != NULL)
{
new_set->ref_count++;
}
}
}
}
return error;
}
/*
* obj_assign_value - This is a generic value assignment function.
* return: error code
* op(in): class or instance pointer
* att(in): attribute descriptor
* mem(in): instance memory pointer (instance attribute only)
* value(in): value to assign
*
* Note:
* It will check the type of the value and call one of the specialized
* assignment functions as necessary.
* This is called by obj_set and by the template assignment function.
*/
int
obj_assign_value (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * value)
{
int error = NO_ERROR;
MOP mop;
MOBJ object = NULL;
if (op == NULL || att == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
if (DB_IS_NULL (value))
{
error = assign_null_value (op, att, mem);
}
else
{
if (TP_IS_SET_TYPE (TP_DOMAIN_TYPE (att->domain)))
{
error = assign_set_value (op, att, mem, db_get_set (value));
}
else
{
if (att->domain->type == tp_Type_object && !op->is_vid && (mop = db_get_object (value))
&& WS_MOP_IS_NULL (mop))
{
error = assign_null_value (op, att, mem);
}
else
{
/* uncomplicated assignment, use the primitive type macros */
if (mem != NULL)
{
error = att->domain->type->setmem (mem, att->domain, value);
if (!error && !att->domain->type->variable_p)
{
if (ws_find (op, &object) == WS_FIND_MOP_DELETED || object == NULL)
{
return ER_OBJ_INVALID_ARGUMENTS;
}
OBJ_SET_BOUND_BIT (object, att->storage_order);
}
}
else
{
pr_clear_value (&att->default_value.value);
pr_clone_value (value, &att->default_value.value);
}
}
}
}
return error;
}
/*
* DIRECT ATTRIBUTE ASSIGNMENT
*/
/*
* obj_set_att -
* return: error code
* op(in): object
* class(in): class structure
* att(in): attribute structure
* value(in): value to assign
* valid(in):
*
* Note:
* This is the common assignment function shared by obj_set() and
* obj_desc_set(). At this point we have direct pointers to the
* class & attribute structures and we can assume that the appropriate
* locks have been obtained.
*/
static int
obj_set_att (MOP op, SM_CLASS * class_, SM_ATTRIBUTE * att, DB_VALUE * value, SM_VALIDATION * valid)
{
int error = NO_ERROR, is_class = 0;
char *mem;
int opin, cpin;
MOP ref_mop;
DB_VALUE *actual;
DB_VALUE base_value;
const char *base_name;
int save, trigstate;
OBJ_TEMPLATE *temp;
MOBJ obj, ref_obj;
MOP class_mop = NULL;
db_make_null (&base_value);
if (op->is_temp)
{
error = obj_set_temp (op, att, value);
}
else
{
/* Check for the presence of triggers or unique constraints, use templates in those cases. */
if (class_->triggers != NULL)
{
is_class = locator_is_class (op, DB_FETCH_WRITE);
if (is_class < 0)
{
return is_class;
}
if (is_class)
{
class_mop = op;
}
else
{
class_mop = ws_class_mop (op);
}
}
trigstate = sm_active_triggers (class_mop, class_, TR_EVENT_ALL);
if (trigstate < 0)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (trigstate || classobj_has_unique_constraint (att->constraints))
{
/* use templates to avoid duplicating trigger code */
temp = obt_edit_object (op);
if (temp == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = obt_assign (temp, att, 0, value, NULL);
if (error == NO_ERROR)
{
error = obt_update (temp, NULL);
}
else
{
obt_quit (temp);
}
}
}
else
{
/*
* simple, single valued update without triggers,
* avoid template overhead
*/
if (op->is_vid)
{
if (class_->class_type == SM_VCLASS_CT)
{
if (vid_is_updatable (op))
{
ref_mop = vid_get_referenced_mop (op);
if (ref_mop)
{
/*
* lock the object for update, this also ensures the class
* gets cached in the MOP which is important for the
* following usage of ref_mop->class
*/
if (au_fetch_instance_force (ref_mop, &ref_obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION) !=
NO_ERROR)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
/*
* some attributes may not be updatable
* even if the instance itself is updatable.
*/
if (db_is_updatable_attribute (op, att->header.name))
{
/* could have att/descriptor versions of these */
error =
mq_update_attribute (ws_class_mop (op), att->header.name, ws_class_mop (ref_mop), value,
&base_value, &base_name, DB_AUTH_UPDATE);
if (error != NO_ERROR)
{
return error;
}
else
{
AU_DISABLE (save);
/* could use att/descriptor interface here */
error = obj_set (ref_mop, base_name, &base_value);
AU_ENABLE (save);
return error;
}
}
else
{
error = ER_IT_ATTR_NOT_UPDATABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
}
}
else
{
error = ER_HEAP_UNKNOWN_OBJECT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
}
}
else
{
error = ER_IT_NOT_UPDATABLE_STMT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
}
return error;
}
}
/* assume class locks are good, get memory offset */
mem = NULL;
if (att->header.name_space == ID_ATTRIBUTE)
{
if (au_fetch_instance (op, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_UPDATE))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
/* must call this when updating instances */
ws_class_has_object_dependencies (ws_class_mop (op));
mem = (char *) (((char *) obj) + att->offset);
}
/*
* now that we have a memory pointer into the object, must pin it
* to prevent workspace flush from destroying it
*/
ws_pin_instance_and_class (op, &opin, &cpin);
if (error == NO_ERROR)
{
actual = obt_check_assignment (att, value, valid, 0);
if (actual == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = obj_assign_value (op, att, mem, actual);
if (actual != value)
{
pr_free_ext_value (actual);
}
#if defined(ENABLE_UNUSED_FUNCTION)
if ((error == NO_ERROR) && (op->is_vid))
{
error = vid_record_update (op, class_, att);
}
#endif
}
}
ws_restore_pin (op, opin, cpin);
}
}
return error;
}
/*
* obj_set - This is the external function for assigning the value of an attribute.
* return: error code
* op(in): class or instance pointer
* name(in): attribute
* value(in):value to assign
*
* Note:
* It will locate the attribute, perform type validation, and make
* the assignment if everything looks ok. If the op argument is a class
* object, it will assign a value to a class attribute. If the op
* argument is an instance object, it will assign a value to either
* a normal or shared attribute.
*/
int
obj_set (MOP op, const char *name, DB_VALUE * value)
{
int error = NO_ERROR;
SM_ATTRIBUTE *att;
SM_CLASS *class_;
if ((op == NULL) || (name == NULL) || ((value != NULL) && (DB_VALUE_TYPE (value) > DB_TYPE_LAST)))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = find_attribute (&class_, &att, op, name, 1);
if (error == NO_ERROR)
{
error = obj_set_att (op, class_, att, value, NULL);
}
}
return (error);
}
/*
* obj_desc_set - This is similar to obj_set() excpet that the attribute is
* identified with a descriptor rather than a name.
* return: error code
* op(in): object
* desc(in): attribute descriptor
* value(in): value to assign
*
* Note:
* Once the actual class & attribute structures are located, it calls
* obj_set_att() to do the work.
*/
int
obj_desc_set (MOP op, SM_DESCRIPTOR * desc, DB_VALUE * value)
{
int error = NO_ERROR;
SM_ATTRIBUTE *att;
SM_CLASS *class_;
if ((op == NULL) || (desc == NULL) || ((value != NULL) && (DB_VALUE_TYPE (value) > DB_TYPE_LAST)))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
/* map the descriptor into an actual pair of class/attribute structures */
if (sm_get_descriptor_component (op, desc, 1, &class_, (SM_COMPONENT **) (&att)))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
error = obj_set_att (op, class_, att, value, desc->valid);
}
return error;
}
/*
* obj_set_shared - This is like obj_set except that it only looks
* for shared attributes.
* return: error code
* op(in): class or instance pointer
* name(in): shared attribute name
* value(in): value to assign
*
* Note:
* This is only necessary for setting shared attributes when given only
* a class object. obj_set when given a class object will only assign
* values to class attributes, if you need to assign a shared attribute
* value instead, you must call this function.
* Triggers are not active here.
* I'm not sure what the behavior of this should be since we aren't
* invoking the update on any particular instance.
* We don't have a descriptor interface for this since obj_desc_set()
*/
int
obj_set_shared (MOP op, const char *name, DB_VALUE * value)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_ATTRIBUTE *att;
DB_VALUE *actual;
/* misc arg checking, need to have optomized versions of this for the interpreter */
if ((op == NULL) || (name == NULL) || ((value != NULL) && (DB_VALUE_TYPE (value) > DB_TYPE_LAST)))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
/* since classes are implicitly pinned, don't have to worry about losing the class here, */
error = find_shared_attribute (&class_, &att, op, name, 1);
if (error == NO_ERROR)
{
actual = obt_check_assignment (att, value, NULL, 0);
if (actual == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = obj_assign_value (op, att, NULL, value);
if (actual != value)
{
(void) pr_free_ext_value (actual);
}
}
}
}
return (error);
}
/*
* VALUE ACCESSORS
*/
/*
* get_object_value - Work function for obj_get_value.
* return: int
* op(in): class or instance pointer
* att(in): attribute descriptor
* mem(in): instance memory pointer (only if instance attribute)
* source(out): source value container
* dest(out): destination value container
*
* Note:
* This is the primitive accessor for "object" valued attributes.
* The main addition here over other attribute types is that it
* will check for deleted object references and convert these to
* NULL for return.
*/
static int
get_object_value (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * source, DB_VALUE * dest)
{
MOP current;
DB_VALUE curval;
int status;
MOBJ object;
int rc = NO_ERROR;
/* use class/shared value if alternate source isn't provided */
if (mem == NULL && source == NULL)
{
source = &att->default_value.value;
}
current = NULL;
if (mem != NULL)
{
db_make_object (&curval, NULL);
if (att->domain->type->getmem (mem, att->domain, &curval))
{
ASSERT_ERROR_AND_SET (rc);
return rc;
}
current = db_get_object (&curval);
}
else if (TP_DOMAIN_TYPE (att->domain) == DB_VALUE_TYPE (source))
{
current = db_get_object (source);
}
/* check for existence of the object this is expensive so only do this if enabled by a parameter. */
if (current != NULL && current->object == NULL && !WS_IS_DELETED (current))
{
if (WS_ISVID (current))
{
/* Check that this operation is not coming from vid workspace management. This context implies that the
* object will not be passed directly to an application. An operation being done may be an object flush, and
* it is undesirable for a side effect of flushing to be fetching more objects, particularly when fetching an
* object can cause flushing and then infinite recursion. */
if (!vid_inhibit_null_check)
{
rc = au_fetch_instance (current, &object, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION (), AU_SELECT);
}
/*
* do NOT mark current as deleted because the fetch may
* have encountered an error which needs to be returned!
*/
}
else
{
status = locator_does_exist_object (current, DB_FETCH_READ);
if (status == LC_DOESNOT_EXIST)
{
WS_SET_DELETED (current);
}
else if (status == LC_ERROR)
{
ASSERT_ERROR_AND_SET (rc);
return rc;
}
}
}
if (current != NULL && WS_IS_DELETED (current))
{
/* convert deleted MOPs to NULL values */
db_make_null (dest);
/*
* set the attribute value so we don't hit this condition again,
* note that this doesn't dirty the object
*/
/* A comm error might cause a fetch error on an existing object. */
if (!WS_ISVID (current))
{
if (mem != NULL)
{
if (att->domain->type->setmem (mem, att->domain, NULL))
{
ASSERT_ERROR_AND_SET (rc);
return rc;
}
OBJ_CLEAR_BOUND_BIT (op->object, att->storage_order);
}
else
{
db_make_null (source);
}
}
}
else
{
if (current != NULL)
{
db_make_object (dest, current);
}
else
{
db_make_null (dest);
}
}
return rc;
}
/*
* get_set_value - Work function for obj_get_value.
* return: int
* op(in): class or instance pointer
* att(in): attirubte descriptor
* mem(in): instance memory pointer (only for instance attribute)
* source(out): source value container
* dest(out): destination value container
*
* Note:
* This is the primitive accessor for set valued attributes.
* This will make sure the set structure is stamped with the MOP of the
* owning object and the attribute id of the attribute that points to
* it. This is so we can get back to this attribute if someone tries
* to do destructive operations to the set descriptor.
*/
static int
get_set_value (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * source, DB_VALUE * dest)
{
SETREF *set;
DB_VALUE setval;
MOP owner;
if (op == NULL || att == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
/* use class/shared value if alternate source isn't provided */
if (mem == NULL && source == NULL)
{
source = &att->default_value.value;
}
/* get owner and current value */
set = NULL;
owner = op;
if (mem != NULL)
{
db_value_domain_init (&setval, TP_DOMAIN_TYPE (att->domain), DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
if (att->domain->type->getmem (mem, att->domain, &setval))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
set = db_get_set (&setval);
db_value_put_null (&setval);
}
else
{
int is_class = locator_is_class (op, DB_FETCH_READ);
if (is_class < 0)
{
return is_class;
}
/* note, we may have a temporary OP here ! */
if (!is_class)
{
owner = ws_class_mop (op); /* shared attribute, owner is class */
}
if (TP_DOMAIN_TYPE (att->domain) == DB_VALUE_TYPE (source))
{
set = db_get_set (source);
/* KLUDGE: shouldn't be doing this at this level */
if (set != NULL)
{
set->ref_count++;
}
}
}
/*
* make sure set has proper ownership tags, this shouldn't happen
* in normal circumstances
*/
if (set != NULL && !ws_is_same_object (set->owner, owner))
{
if (set_connect (set, owner, att->id, att->domain))
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
/* convert NULL sets to DB_TYPE_NULL */
if (set == NULL)
{
db_make_null (dest);
}
else
{
switch (TP_DOMAIN_TYPE (att->domain))
{
case DB_TYPE_SET:
default:
db_make_set (dest, set);
break;
case DB_TYPE_MULTISET:
db_make_multiset (dest, set);
break;
case DB_TYPE_SEQUENCE:
db_make_sequence (dest, set);
break;
}
}
return NO_ERROR;
}
/*
* obj_get_value -
* return: int
* op(in): class or instance pointer
* att(in): attribute descriptor
* mem(in): instance memory pointer (only for instance attribute)
* source(out): alternate source value (optional)
* dest(out): destionation value container
*
* Note:
* This is the basic generic function for accessing an attribute
* value. It will call one of the specialized accessor functions above
* as necessary.
*/
int
obj_get_value (MOP op, SM_ATTRIBUTE * att, void *mem, DB_VALUE * source, DB_VALUE * dest)
{
int error = NO_ERROR;
MOBJ object = NULL;
if (op == NULL || att == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
/* use class/shared value if alternate source isn't provided */
if (mem == NULL && source == NULL)
{
source = &att->default_value.value;
}
if ((ws_find (op, &object) == WS_FIND_MOP_DELETED) || object == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
/* first check the bound bits */
if (!att->domain->type->variable_p && mem != NULL && OBJ_GET_BOUND_BIT (object, att->storage_order) == 0)
{
db_make_null (dest);
}
else
{
if (TP_IS_SET_TYPE (TP_DOMAIN_TYPE (att->domain)))
{
error = get_set_value (op, att, (char *) mem, source, dest);
}
else if (att->domain->type == tp_Type_object)
{
error = get_object_value (op, att, (char *) mem, source, dest);
}
else
{
if (mem != NULL)
{
error = att->domain->type->getmem (mem, att->domain, dest);
}
else
{
error = pr_clone_value (source, dest);
}
if (error == NO_ERROR)
{
if (DB_VALUE_TYPE (dest) == DB_TYPE_STRING && db_get_string (dest) == NULL)
{
db_make_null (dest);
}
}
}
}
return error;
}
/*
* obj_get_att - This is a common attribute retriveal function shared by
* obj_get & obj_desc_get.
* return: error code
* op(in): object
* class(in): class structure
* att(in): attribute structure
* value(out): value container(output)
*
* Note:
* It operates assuming that we now
* have direct pointers to the class & attribute structures and that
* the appropriate locks have been obtained.
* It handles the difference between temporary, virtual, and normal
* instnace MOPs.
*/
int
obj_get_att (MOP op, SM_CLASS * class_, SM_ATTRIBUTE * att, DB_VALUE * value)
{
int error = NO_ERROR;
SM_CLASS *ref_class;
char *mem;
int opin, cpin;
MOP ref_mop;
MOBJ obj;
if (op->is_temp)
{
error = obj_get_temp (op, class_, att, value);
}
else
{
if (op->is_vid)
{
if (class_->class_type == SM_VCLASS_CT)
{
if (vid_is_updatable (op))
{
ref_mop = vid_get_referenced_mop (op);
if (ref_mop)
{
error = au_fetch_class_force (ref_mop, &ref_class, AU_FETCH_READ);
if (error == NO_ERROR)
{
return mq_get_attribute (ws_class_mop (op), att->header.name, ws_class_mop (ref_mop), value,
ref_mop);
}
else
{
return error;
}
}
else
{
error = ER_HEAP_UNKNOWN_OBJECT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
}
else
{
/* fall through to "normal" fetch for non-updatable */
}
}
}
/* fetch the instance if necessary */
mem = NULL;
if (att->header.name_space == ID_ATTRIBUTE)
{
/* fetch the instance and caluclate memory offset */
if (au_fetch_instance_force (op, &obj, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION ()) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
mem = (char *) (((char *) obj) + att->offset);
}
ws_pin_instance_and_class (op, &opin, &cpin);
error = obj_get_value (op, att, mem, NULL, value);
ws_restore_pin (op, opin, cpin);
}
return error;
}
/*
* obj_desc_get - This retrieves the value of an attribute using a
* descriptor rather than an attribute name.
* return:
* op(in): object
* desc(in): attribute descriptor
* value(out): value container (returned)
*
* Note:
* Descriptors are good for repetitive access to the same attribute.
*/
int
obj_desc_get (MOP op, SM_DESCRIPTOR * desc, DB_VALUE * value)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_ATTRIBUTE *att;
if ((op == NULL) || (desc == NULL) || (value == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
/* map the descriptor into an actual pair of class/attribute structures */
if (sm_get_descriptor_component (op, desc, 0, &class_, (SM_COMPONENT **) (&att)) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
error = obj_get_att (op, class_, att, value);
}
return error;
}
/*
* obj_get - This is the external function for accessing attribute values.
* return: error code
* op(in): class or instance pointer
* name(in): attribute name
* value(out): destination value container
*
* Note:
* If the named attribute is found, the value is returned through the
* supplied value container.
*/
int
obj_get (MOP op, const char *name, DB_VALUE * value)
{
int error = NO_ERROR;
SM_ATTRIBUTE *att;
SM_CLASS *class_;
if ((op == NULL) || (name == NULL) || (value == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = find_attribute (&class_, &att, op, name, 0);
if (error == NO_ERROR)
{
error = obj_get_att (op, class_, att, value);
}
}
return error;
}
/*
* obj_get_shared - This is used to access the value of a shared attribute only.
* return: error code
* op(in): class or instance pointer
* name(in): shared attribute name
* value(out): destination value container
*
* Note :
* This is used only in cases where you have the MOP of a class and you want
* to get the value of a shared attribute. Since the default behavior
* of obj_get is to look for class attributes when given a class mop, you
* must use this function to get shared attribute values from a class
* mop.
* Note that there is no need to have a descriptor version of this
* function, obj_desc_get() will work for shared attributes provided
* that a shared attribute descriptor is being used.
*/
int
obj_get_shared (MOP op, const char *name, DB_VALUE * value)
{
int error = NO_ERROR;
SM_ATTRIBUTE *att;
SM_CLASS *class_;
if ((op == NULL) || (name == NULL) || (value == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = find_shared_attribute (&class_, &att, op, name, 0);
if (error == NO_ERROR)
{
obj_get_value (op, att, NULL, NULL, value);
}
}
return error;
}
/*
*
* OBJECT ACCESS WITH PSEUDO PATH EXPRESSION
*
*
*/
/*
* obj_get_path -
* return: error
* object(in): class or instance
* attpath(in): a simple attribute name or path expression
* value(out): value container to hold the returned value
*
*/
int
obj_get_path (DB_OBJECT * object, const char *attpath, DB_VALUE * value)
{
int error;
char buf[512];
char *token, *end;
char delimiter, nextdelim;
DB_VALUE temp_value;
int index;
error = NO_ERROR;
(void) strcpy (&buf[0], attpath);
delimiter = '.'; /* start with implicit dot */
db_make_object (&temp_value, object);
for (token = &buf[0]; char_isspace (*token) && *token != '\0'; token++);
end = token;
while (delimiter != '\0' && error == NO_ERROR)
{
nextdelim = '\0';
if (delimiter == '.')
{
if (DB_VALUE_TYPE (&temp_value) != DB_TYPE_OBJECT)
{
ERROR0 (error, ER_OBJ_INVALID_OBJECT_IN_PATH);
}
else
{
for (end = token; !char_isspace (*end) && *end != '\0';)
{
if ((*end != '.') && (*end != '['))
{
end++;
}
else
{
nextdelim = *end;
*end = '\0';
}
}
if (token == end)
{
ERROR0 (error, ER_OBJ_INVALID_PATH_EXPRESSION);
}
else
{
error = obj_get (db_get_object (&temp_value), token, &temp_value);
}
}
}
else if (delimiter == '[')
{
DB_TYPE temp_type;
temp_type = DB_VALUE_TYPE (&temp_value);
if (!TP_IS_SET_TYPE (temp_type))
{
ERROR0 (error, ER_OBJ_INVALID_SET_IN_PATH);
}
else
{
for (end = token; char_isdigit (*end) && *end != '\0'; end++)
;
nextdelim = *end;
*end = '\0';
if (end == token)
{
ERROR0 (error, ER_OBJ_INVALID_INDEX_IN_PATH);
}
else
{
index = atoi (token);
if (temp_type == DB_TYPE_SEQUENCE)
{
error = db_seq_get (db_get_set (&temp_value), index, &temp_value);
}
else
{
error = db_set_get (db_get_set (&temp_value), index, &temp_value);
}
if (error == NO_ERROR)
{
for (++end; nextdelim != ']' && nextdelim != '\0'; nextdelim = *end++)
;
if (nextdelim != '\0')
{
nextdelim = *end;
}
}
}
}
}
else
{
ERROR0 (error, ER_OBJ_INVALID_PATH_EXPRESSION);
}
/* next iteration */
delimiter = nextdelim;
token = end + 1;
}
if (error == NO_ERROR)
{
*value = temp_value;
}
return error;
}
/*
*
* TEMPORARY OBJECT ACCESS
*
*
*/
/*
* obj_get_temp - This is called by obj_get() after it is determined
* that the MOP is really a temporary object MOP.
* return: error code
* obj(in): temporary MOP
* class(in): class pointer
* att(in): attribute structure
* value(out): value container
*
* Note :
* We get the associated template and look for an assignment that
* matches the attribute name. If one is not found, we then
* call obj_get() on the REAL object in order to get the current
* attribute value.
*/
static int
obj_get_temp (DB_OBJECT * obj, SM_CLASS * class_, SM_ATTRIBUTE * att, DB_VALUE * value)
{
int error = NO_ERROR;
OBJ_TEMPLATE *temp;
OBJ_TEMPASSIGN *assignment;
DB_VALUE *src;
MOP object;
if (obj->class_mop == NULL || obj->object == NULL)
{
ERROR0 (error, ER_OBJ_INVALID_TEMP_OBJECT);
}
else
{
temp = (OBJ_TEMPLATE *) (obj->object);
/* locate an assignment for this attribute in the template */
assignment = temp->assignments[att->order];
if (assignment != NULL)
{
/*
* If this is a "new" object, return the assignment value, otherwise
* return the saved value.
*/
if (temp->is_old_template)
{
src = assignment->old_value;
}
else
{
src = assignment->variable;
}
/*
* Note that for sets, the ownership may get tagged with
* a temporary object
*/
error = obj_get_value (obj, att, NULL, src, value);
}
else
{
/*
* Couldn't find it in the template, get it out of the real object.
* Since we've already done some of the work, could optimize
* the value fetch a bit. Make sure we use the base object so
* the value translation isn't performed.
*/
object = OBT_BASE_OBJECT (temp);
if (object != NULL)
{
error = obj_get_att (object, class_, att, value);
}
else
{
/*
* there was no base object so we must be performing an insertion,
* in this case, the value is considered to be NULL
*/
db_make_null (value);
}
}
}
return error;
}
/*
* obj_set_temp - This is used to change a value in a temporary object.
* return: error code
* obj(in): temporary object MOP
* att(in): attribute name
* value(out): value container
*
* Note:
* It is called by obj_set() when a temporary MOP is passed.
* This is available ONLY for the "new" object in a BEFORE INSERT or
* BEFORE UPDATE trigger. In this case, the trigger action can
* use db_put() to change the values in the template. Since this is
* a straightforward template addition, we just call obt_assign() to
* set the new value.
* If this is not a "new" template an error is signalled because
* it is not meaningful to change the values of "old" objects.
*/
static int
obj_set_temp (DB_OBJECT * obj, SM_ATTRIBUTE * att, DB_VALUE * value)
{
int error = NO_ERROR;
OBJ_TEMPLATE *temp;
if (obj->class_mop == NULL || obj->object == NULL)
{
ERROR0 (error, ER_OBJ_INVALID_TEMP_OBJECT);
}
else
{
temp = (OBJ_TEMPLATE *) (obj->object);
if (temp->is_old_template)
{
/* can't update templates containing "old" state */
ERROR0 (error, ER_OBJ_INVALID_TEMP_OBJECT);
}
else
{
/*
* Treat this like a normal template assignment. Remember,
* this template may have been created on a virtual class and if
* so, it is expecting attribute names on the virtual class rather
* than the base class. Pass the "base_assignment" flag of non-zero
* here so that obt_assign knows that we don't want to translate
* the values.
*/
error = obt_assign (temp, att, 1, value, NULL);
}
}
return error;
}
/*
*
* OBJECT CREATION AND DELETION
*
*
*/
/*
* obj_alloc - Allocate and initialize storage for an instance block.
* return: instance block
* class(in): class structure
* bound_bit_status(in): nitial state for bound bits
*
* Note:
* The bit_status argument has the initial state for the bound bits.
* If it is zero, all bits are off, if it is non-zero, all bits are
* on.
*/
char *
obj_alloc (SM_CLASS * class_, int bound_bit_status)
{
WS_OBJECT_HEADER *header;
SM_ATTRIBUTE *att;
char *obj, *mem;
unsigned int *bits;
int nwords, i;
obj = (char *) db_ws_alloc (class_->object_size);
if (obj != NULL)
{
/* initialize system header fields */
header = (WS_OBJECT_HEADER *) obj;
header->chn = NULL_CHN;
/* init the bound bit vector */
if (class_->fixed_count)
{
bits = (unsigned int *) (obj + OBJ_HEADER_BOUND_BITS_OFFSET);
nwords = OR_BOUND_BIT_WORDS (class_->fixed_count);
for (i = 0; i < nwords; i++)
{
if (bound_bit_status)
{
bits[i] = 0xFFFFFFFF;
}
else
{
bits[i] = 0;
}
}
}
/* clear the object */
for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
{
mem = obj + att->offset;
att->domain->type->initmem (mem, att->domain);
}
}
return obj;
}
/*
* obj_create - Creates a new instance of a class.
* return: new object
* classop(in): class or instance pointer
*
* Note:
* Formerly, this allocated the instance and assigned the default
* values directly into the object. Now that triggers and
* virtual classes complicate things, we use an insert template
* for all creations.
*/
MOP
obj_create (MOP classop)
{
OBJ_TEMPLATE *obj_template;
MOP new_mop;
new_mop = NULL;
obj_template = obt_def_object (classop);
if (obj_template != NULL)
{
/* remember to disable the NON NULL integrity constraint checking */
if (obt_update_internal (obj_template, &new_mop, 0))
{
obt_quit (obj_template);
}
}
return (new_mop);
}
/*
* obj_create_by_name - Create an instance of a class given the class name.
* return: new object
* name(in): class name
*
*/
MOP
obj_create_by_name (const char *name)
{
MOP class_mop, obj;
obj = NULL;
if (name != NULL)
{
class_mop = sm_find_class (name);
if (class_mop != NULL)
{
obj = obj_create (class_mop);
}
}
return (obj);
}
/*
* obj_copy -
* return: new object
* op(in): object to copy
*
* Note:
* Utility function to do a simple object copy. This only does a single
* level copy.
* Formerly, this did a rather low level optimized copy of the object.
* Now with virtual objects & triggers etc., we simply create an insert
* template with the current values of the object.
* This isn't particularly effecient but not very many people use
* object copy anyway.
*
*/
MOP
obj_copy (MOP op)
{
OBJ_TEMPLATE *obj_template;
MOP new_mop;
SM_CLASS *class_;
SM_ATTRIBUTE *att;
MOBJ src;
DB_VALUE value;
new_mop = NULL;
db_make_null (&value);
/* op must be an object */
if (op != NULL)
{
int is_class = locator_is_class (op, DB_FETCH_CLREAD_INSTWRITE);
if (is_class < 0)
{
return NULL;
}
if (is_class)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
if (au_fetch_class (op, &class_, AU_FETCH_READ, AU_INSERT) != NO_ERROR)
return NULL;
/* do this so that we make really sure that op->class is set up */
if (au_fetch_instance (op, &src, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION (), AU_SELECT) != NO_ERROR)
return NULL;
obj_template = obt_def_object (ws_class_mop (op));
if (obj_template != NULL)
{
for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
{
if (obj_get_att (op, class_, att, &value) != NO_ERROR)
goto error;
if (obt_assign (obj_template, att, 0, &value, NULL) != NO_ERROR)
goto error;
(void) pr_clear_value (&value);
}
/* leaves new NULL if error */
if (obt_update_internal (obj_template, &new_mop, 0) != NO_ERROR)
{
obt_quit (obj_template);
}
}
return new_mop;
error:
obt_quit (obj_template);
return NULL;
}
/*
* obj_free_memory - This frees all of the storage allocated for an object.
* return: none
* class(in):
* obj(in): object pointer
*
* Note:
* It will be called indirectly by the workspace manager when an
* object is decached.
*/
void
obj_free_memory (SM_CLASS * class_, MOBJ obj)
{
SM_ATTRIBUTE *att;
char *mem;
for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
{
mem = ((char *) obj) + att->offset;
att->domain->type->freemem (mem);
}
db_ws_free (obj);
}
/*
* obj_delete - This is the external function for deleting an object.
* return: error code
* op(in): instance pointer
*
* Note:
* You cannot delete classes with this function, only instances. This will
* decache the instance and mark the MOP as deleted but will not free the
* MOP since there may be references to it in the application. The mop will
* be garbage collected later.
*/
int
obj_delete (MOP op)
{
int error = NO_ERROR, is_class = 0;
SM_CLASS *class_ = NULL;
SM_CLASS *base_class = NULL;
DB_OBJECT *base_op = NULL;
char *obj = NULL;
int pin = 0;
int pin2 = 0;
bool unpin_on_error = false;
TR_STATE *trstate = NULL;
/* op must be an object */
if (op != NULL)
{
is_class = locator_is_class (op, DB_FETCH_WRITE);
if (is_class < 0)
{
error = is_class;
goto error_exit;
}
if (is_class > 0)
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
goto error_exit;
}
}
else
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
goto error_exit;
}
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_DELETE);
if (error != NO_ERROR)
{
goto error_exit;
}
error = au_fetch_instance (op, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_DELETE);
if (error != NO_ERROR)
{
goto error_exit;
}
/*
* Note that if "op" is a VMOP, au_fetch_instance () will have returned
* "obj" as a pointer to the BASE INSTANCE memory which is not the instance
* associated with "op". When this happens we need to get the base MOP so
* that it can be passed down to other functions that need to look at the
* "obj" instance memory block.
*/
base_op = op;
if (op->is_vid && class_->class_type == SM_VCLASS_CT)
{
/*
* This is a view, get the base MOP.
* What happens here if this is a non-updatable view?
*/
base_op = vid_get_referenced_mop (op);
if (base_op == NULL)
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
goto error_exit;
}
au_fetch_class (base_op, &base_class, AU_FETCH_READ, AU_DELETE);
}
/* We need to keep it pinned for the duration of trigger processing. */
pin = ws_pin (op, 1);
if (base_op != NULL && base_op != op)
{
pin2 = ws_pin (base_op, 1);
}
unpin_on_error = true;
/* Run BEFORE triggers */
if (base_class != NULL)
{
error = tr_prepare_class (&trstate, base_class->triggers, base_op, TR_EVENT_DELETE);
if (error != NO_ERROR)
{
goto error_exit;
}
error = tr_before_object (trstate, base_op, NULL);
if (error != NO_ERROR)
{
goto error_exit;
}
/* in some cases, the object has been decached in before trigger. we need fetch it again. */
if (base_op->decached)
{
error = au_fetch_class (base_op, &base_class, AU_FETCH_READ, AU_DELETE);
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
else
{
error = tr_prepare_class (&trstate, class_->triggers, ws_class_mop (op), TR_EVENT_DELETE);
if (error != NO_ERROR)
{
goto error_exit;
}
error = tr_before_object (trstate, op, NULL);
if (error != NO_ERROR)
{
goto error_exit;
}
/* in some cases, the object has been decached in before trigger. we need fetch it again. */
if (op->decached)
{
error = au_fetch_instance (op, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_DELETE);
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
/*
* Unpin this now since the remaining operations will mark the instance as
* deleted and it doesn't make much sense to have pinned & deleted objects.
*/
(void) ws_pin (op, pin);
if (base_op != NULL && base_op != op)
{
(void) ws_pin (base_op, pin2);
}
unpin_on_error = false;
/*
* We don't need to decache the object as it will be decached when the mop
* is GC'd in the usual way.
*/
if (op->is_vid)
{
vid_rem_instance (op);
}
else
{
locator_remove_instance (op);
}
/* Run AFTER triggers */
if (trstate != NULL)
{
error = tr_after_object (trstate, NULL, NULL);
if (error != NO_ERROR)
{
goto error_exit;
}
}
return error;
error_exit:
if (unpin_on_error)
{
/* trigger failure, remember to unpin */
(void) ws_pin (op, pin);
if (base_op != NULL && base_op != op)
{
(void) ws_pin (base_op, pin2);
}
unpin_on_error = false;
}
return error;
}
/*
* METHOD CALL SUPPORT FUNCTIONS
*
*/
/*
* argstate_from_list - Builds a cannonical argument state structure
* from a DB_VALUE_LIST.
* return: none
* state(in): method argument state
* arglist(in): argument list
*
*/
static void
argstate_from_list (ARGSTATE * state, DB_VALUE_LIST * arglist)
{
DB_VALUE_LIST *arg;
int i;
for (i = 0, arg = arglist; arg != NULL && i < OBJ_MAX_ARGS; arg = arg->next, i++)
{
state->values[i] = &(arg->val);
state->save[i] = NULL;
}
state->nargs = i;
state->overflow = arg;
state->free_overflow = 0;
state->save_overflow = NULL;
for (i = 0; arg != NULL; arg = arg->next, i++)
;
state->noverflow = i;
}
/*
* argstate_from_array - Builds a cannonical argument state
* from an array of DB_VALUEs
* return: none
* state(in): method argument state
* argarray(in): array of DB_VALUE pointers
*
*/
static void
argstate_from_array (ARGSTATE * state, DB_VALUE ** argarray)
{
int i, j;
state->overflow = NULL;
state->noverflow = 0;
state->save_overflow = NULL;
state->free_overflow = 0;
if (argarray == NULL)
{
state->nargs = 0;
}
else
{
for (i = 0; argarray[i] != NULL && i < OBJ_MAX_ARGS; i++)
{
state->values[i] = argarray[i];
state->save[i] = NULL;
}
state->nargs = i;
/* need to handle overflow arguments ! */
for (j = 0; argarray[i] != NULL; i++, j++)
;
state->noverflow = j;
}
}
/*
* argstate_from_va - Builds a cannonical argument state
* from a va_list of arguments.
* return: none
* state(in): method argument state
* args(in): va_list style argument list
* nargs(in): expected number of arguments
*
*/
static void
argstate_from_va (ARGSTATE * state, va_list args, int nargs)
{
int i;
state->nargs = nargs;
state->overflow = NULL;
state->free_overflow = 0;
state->save_overflow = NULL;
for (i = 0; i < nargs && i < OBJ_MAX_ARGS; i++)
{
state->values[i] = va_arg (args, DB_VALUE *);
state->save[i] = NULL;
}
/* need to handle overflow arguments ! */
state->noverflow = nargs - i;
}
/*
* cleanup_argstate -
* return: none
* state(in): method argument state
*
* Note:
* This is called after an argstate structure is no longer needed.
* It frees any additional resources that were required during the
* processing of the method call.
* Currently this will consist only of argument values that had to
* be coerced from their original values.
*/
static void
cleanup_argstate (ARGSTATE * state)
{
int i;
/* free values for arguments that were coerced */
for (i = 0; i < state->nargs; i++)
{
if (state->save[i] != NULL)
{
/* we have a coerced value in the corresponding value slot */
(void) pr_free_ext_value (state->values[i]);
state->values[i] = state->save[i];
state->save[i] = NULL;
}
}
/* free the overflow list if it was created from a "va" call */
}
/*
* call_method - This makes the actual call to the method function and passes
* the arguments.
* The arguments are taken from the value array in the argument
* state structure
* return: int
* method(in):method function pointer
* obj(in):"self" object of the method
* returnval(in):return value pointer
* nargs(in):
* values(in):
* overflow(in):
*
* Note:
*
* Forcing NULL into the first 4 arguments was necessary at some
* point because the old interpreter didn't do any argument checking.
* It may not be necessary any more but be very careful before removing
* it.
*
*/
static int
call_method (METHOD_FUNCTION method, MOP obj, DB_VALUE * returnval, int nargs, DB_VALUE ** values,
DB_VALUE_LIST * overflow)
{
int error = NO_ERROR;
obj_Method_call_level++;
if (obj_Method_call_level == 1)
{ /* not nested method call */
if (obj_Method_error_msg)
{
free_and_init (obj_Method_error_msg);
}
}
if (!forge_flag_pat)
{
db_make_null (returnval);
}
switch (nargs)
{
case 0:
((METHOD_FUNC_ARG4) (*method)) (obj, returnval, NULL, NULL, NULL, NULL);
break;
case 1:
((METHOD_FUNC_ARG4) (*method)) (obj, returnval, values[0], NULL, NULL, NULL);
break;
case 2:
((METHOD_FUNC_ARG4) (*method)) (obj, returnval, values[0], values[1], NULL, NULL);
break;
case 3:
((METHOD_FUNC_ARG4) (*method)) (obj, returnval, values[0], values[1], values[2], NULL);
break;
case 4:
((METHOD_FUNC_ARG4) (*method)) (obj, returnval, values[0], values[1], values[2], values[3]);
break;
case 5:
((METHOD_FUNC_ARG5) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4]);
break;
case 6:
((METHOD_FUNC_ARG6) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5]);
break;
case 7:
((METHOD_FUNC_ARG7) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6]);
break;
case 8:
((METHOD_FUNC_ARG8) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7]);
break;
case 9:
((METHOD_FUNC_ARG9) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8]);
break;
case 10:
((METHOD_FUNC_ARG10) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9]);
break;
case 11:
((METHOD_FUNC_ARG11) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10]);
break;
case 12:
((METHOD_FUNC_ARG12) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11]);
break;
case 13:
((METHOD_FUNC_ARG13) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12]);
break;
case 14:
((METHOD_FUNC_ARG14) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13]);
break;
case 15:
((METHOD_FUNC_ARG15) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14]);
break;
case 16:
((METHOD_FUNC_ARG16) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15]);
break;
case 17:
((METHOD_FUNC_ARG17) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16]);
break;
case 18:
((METHOD_FUNC_ARG18) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17]);
break;
case 19:
((METHOD_FUNC_ARG19) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18]);
break;
case 20:
((METHOD_FUNC_ARG20) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19]);
break;
case 21:
((METHOD_FUNC_ARG21) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20]);
break;
case 22:
((METHOD_FUNC_ARG22) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21]);
break;
case 23:
((METHOD_FUNC_ARG23) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22]);
break;
case 24:
((METHOD_FUNC_ARG24) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23]);
break;
case 25:
((METHOD_FUNC_ARG25) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24]);
break;
case 26:
((METHOD_FUNC_ARG26) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25]);
break;
case 27:
((METHOD_FUNC_ARG27) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26]);
break;
case 28:
((METHOD_FUNC_ARG28) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26], values[27]);
break;
case 29:
((METHOD_FUNC_ARG29) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26], values[27], values[28]);
break;
case 30:
((METHOD_FUNC_ARG30) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26], values[27], values[28], values[29]);
break;
case 31:
((METHOD_FUNC_ARG31) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26], values[27], values[28], values[29], values[30]);
break;
case 32:
((METHOD_FUNC_ARG32) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26], values[27], values[28], values[29], values[30],
values[31]);
break;
default:
((METHOD_FUNC_ARG33) (*method)) (obj, returnval, values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], values[10], values[11], values[12],
values[13], values[14], values[15], values[16], values[17], values[18],
values[19], values[20], values[21], values[22], values[23], values[24],
values[25], values[26], values[27], values[28], values[29], values[30],
values[31], overflow);
break;
}
obj_Method_call_level--;
if (!forge_flag_pat)
if (DB_VALUE_TYPE (returnval) == DB_TYPE_ERROR)
{
error = db_get_error (returnval);
if (error >= 0)
{
/* it's not a system error, it's a user error */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_METHOD_USER_ERROR, 1, error);
error = er_errid ();
}
}
return error;
}
/*
* check_args - This performs argument validation prior to a method call.
* return: error code
* method(in): method
* state(in): argument state
*
* Note :
* If the method has no signature or if the argument list is NULL,
* the values will be passed through unchecked.
*
* This needs to be changed in a number of ways for the next release.
* First, there is no way currently of defining a method that must
* accept NO arguments. The absense of an argument list is taken to
* mean a "wildcard" argument list.
* Second, there needs to be a more formal definition of when an argument
* is passed by reference vs. passed by value. This is especially
* important when set coercion is happening.
* Third, the method argument lists should be stored in a packed array
* for quick lookup.
*/
static int
check_args (SM_METHOD * method, ARGSTATE * state)
{
int error = NO_ERROR;
SM_METHOD_SIGNATURE *sig;
SM_METHOD_ARGUMENT *arg;
DB_VALUE *value;
int i;
TP_DOMAIN_STATUS status;
TP_DOMAIN *dom;
/* assume only one signature */
sig = method->signatures;
if (sig != NULL && sig->args != NULL)
{
for (i = 0; i < state->nargs && error == NO_ERROR; i++)
{
/*
* find the argument matching this value, this should be an array lookup !
* remember the arg index is one based
*/
for (arg = sig->args; arg != NULL && arg->index != i + 1; arg = arg->next);
/*
* if there is no definition for a particular argument, assume it
* is a "wildcard" and will match any domain
*/
if (arg != NULL)
{
/*
* Try to use exact domain matching for method arguments !
*/
dom = tp_domain_select (arg->domain, state->values[i], 0, TP_EXACT_MATCH);
if (dom == NULL)
{
/*
* We don't have an exact match, so try a "near" match, i.e.,
* one where we can get what we want simply by changing a
* string domain (without actually copying the string). This
* is important for trying to keep "output parameters" working.
*/
value = pr_make_ext_value ();
if (value == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
dom = tp_domain_select (arg->domain, state->values[i], 0, TP_STR_MATCH);
if (dom)
{
*value = *state->values[i];
value->need_clear = false;
status = tp_value_coerce (value, value, arg->domain);
}
else
{
status = tp_value_cast (state->values[i], value, arg->domain, 0);
}
if (status == DOMAIN_COMPATIBLE)
{
/* pass the coerced value but remember to free it later */
state->save[i] = state->values[i];
state->values[i] = value;
}
else
{
(void) pr_free_ext_value (value);
}
}
else
{
status = DOMAIN_COMPATIBLE;
}
if (status != DOMAIN_COMPATIBLE)
{
char domain1[MAX_DOMAIN_NAME];
char domain2[MAX_DOMAIN_NAME];
switch (status)
{
case DOMAIN_ERROR:
assert (er_errid () != NO_ERROR);
error = er_errid ();
break;
case DOMAIN_OVERFLOW:
case DOMAIN_INCOMPATIBLE:
default:
error = ER_OBJ_ARGUMENT_DOMAIN_CONFLICT;
tp_domain_name (arg->domain, domain1, MAX_DOMAIN_NAME);
tp_value_domain_name (state->values[i], domain2, MAX_DOMAIN_NAME);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 4, method->header.name, arg->index, domain1,
domain2);
break;
}
}
}
}
}
return (error);
}
/*
* METHOD INVOCATION FUNCTIONS
*
*/
/*
* These all do basically the same thing, the only difference is the
* way in which the arguments are passed.
*/
/*
* obj_send_method_va - This invokes a method where the arguments are
* supplied with a va_list.
* return: error code
* obj(in): object to receive the message
* class(in): class structure
* method(in): method structure
* returnval(out): return value container
* args(in): va_list with arguments
*
* Note:
* It is called by both obj_send_va() and obj_desc_send_va().
*/
static int
obj_send_method_va (MOP obj, SM_CLASS * class_, SM_METHOD * method, DB_VALUE * returnval, va_list args)
{
ARGSTATE state;
int error = NO_ERROR;
SM_METHOD_SIGNATURE *sig;
METHOD_FUNCTION func;
int expected;
func = method->function;
if (func == NULL)
{
error = sm_link_method (class_, method);
func = method->function;
}
if (func != NULL)
{
sig = method->signatures;
/*
* calculate the expected number of arguments
* allow the case where the arg count is set but there are no
* arg definitions, should be an error
*/
expected = (sig != NULL && sig->num_args) ? sig->num_args : OBJ_MAX_ARGS;
/* get the arguments into the cannonical array */
argstate_from_va (&state, args, expected);
/* need to handle this gracefully someday */
if (state.noverflow)
{
ERROR3 (error, ER_OBJ_TOO_MANY_ARGUMENTS, method->header.name, state.nargs + state.noverflow, OBJ_MAX_ARGS);
}
else
{
/* check argument domains if there are any */
if (sig != NULL && sig->args != NULL)
error = check_args (method, &state);
if (error == NO_ERROR)
error = call_method (func, obj, returnval, state.nargs, state.values, state.overflow);
}
cleanup_argstate (&state);
}
return (error);
}
/*
* obj_send_va - Call a method by name with arguments as a va_list
* return: error code
* obj(in): object
* name(in): method name
* returnval(out): return value container
* args(in): argument list
*
*/
int
obj_send_va (MOP obj, const char *name, DB_VALUE * returnval, va_list args)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
bool class_method;
if ((obj == NULL) || (name == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = au_fetch_class (obj, &class_, AU_FETCH_READ, AU_EXECUTE);
if (error == NO_ERROR)
{
/* rare case when its ok to use this macro */
class_method = (IS_CLASS_MOP (obj)) ? true : false;
method = classobj_find_method (class_, name, class_method);
if (method == NULL)
{
ERROR1 (error, ER_OBJ_INVALID_METHOD, name);
}
else
{
error = obj_send_method_va (obj, class_, method, returnval, args);
}
}
}
return error;
}
/*
* obj_desc_send_va - Call a method using a descritor with arguments
* as a va_list.
* return: error code
* obj(in): object
* desc(in): descriptor
* returnval(out): return value container
* args(in): argument list
*
*/
int
obj_desc_send_va (MOP obj, SM_DESCRIPTOR * desc, DB_VALUE * returnval, va_list args)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
if ((obj == NULL) || (desc == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = sm_get_descriptor_component (obj, desc, 0, &class_, (SM_COMPONENT **) (&method));
if (error == NO_ERROR)
{
error = obj_send_method_va (obj, class_, method, returnval, args);
}
}
return error;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* obj_send_stack - This invokes a method where the arguments are passed
* directly on the stack
*
* return :error code
* obj(in): object to receive the message
* name(in): method name
* returnval(out): return value container
*
* Note:
* This invokes a method where the arguments are passed directly
* on the stack. It calls obj_send_va to do the work after buidling
* the va_list from the arguments passed to this function.
*
*/
int
obj_send_stack (MOP obj, const char *name, DB_VALUE * returnval, ...)
{
int error = NO_ERROR;
va_list args;
va_start (args, returnval);
error = obj_send_va (obj, name, returnval, args);
va_end (args);
return (error);
}
/*
* obj_desc_send_stack - Call a method using a descritor with
* arguments on the stack.
*
* return: error code
* obj(in): object
* desc(in): descriptor
* returnval(out): return value container
* args(in): argument list
*
* Note :
* Call a method using a descritor with arguments on the stack.
*
*/
int
obj_desc_send_stack (MOP obj, SM_DESCRIPTOR * desc, DB_VALUE * returnval, ...)
{
int error = NO_ERROR;
va_list args;
va_start (args, returnval);
error = obj_desc_send_va (obj, desc, returnval, args);
va_end (args);
return (error);
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* obj_send_method_list - This invokes a method where the arguments are
* contained in a linked list of DB_VALUE_LIST structures.
* return: error code
* obj(in): object to receive message
* class(in): class structure
* method(in): method structure
* returnval(in): return value container
* arglist(in): argument list
*
* Note:
* Used by both obj_send_list() and obj_desc_send_list().
*/
static int
obj_send_method_list (MOP obj, SM_CLASS * class_, SM_METHOD * method, DB_VALUE * returnval, DB_VALUE_LIST * arglist)
{
int error = NO_ERROR;
ARGSTATE state;
SM_METHOD_SIGNATURE *sig;
METHOD_FUNCTION func;
int expected;
func = method->function;
if (func == NULL)
{
error = sm_link_method (class_, method);
func = method->function;
}
if (func != NULL)
{
sig = method->signatures;
/*
* calculate the expected number of arguments
* allow the case where the arg count is set but there are no
* arg definitions, should be an error
*/
expected = ((sig != NULL && sig->num_args) ? sig->num_args : OBJ_MAX_ARGS);
/* get the arguments into the cannonical array */
argstate_from_list (&state, arglist);
/* need to handle this gracefully someday */
if (state.noverflow)
{
ERROR3 (error, ER_OBJ_TOO_MANY_ARGUMENTS, method->header.name, state.nargs + state.noverflow, OBJ_MAX_ARGS);
}
else
{
/*
* what happens when the actual count doesn't match the expected
* count and there is no domain definition ?
* for now, assume the supplied args are correct
*/
/* check argument domains if there are any */
if (sig != NULL && sig->args != NULL)
{
error = check_args (method, &state);
}
if (error == NO_ERROR)
{
error = call_method (func, obj, returnval, state.nargs, state.values, state.overflow);
}
}
cleanup_argstate (&state);
}
return (error);
}
/*
* obj_send_list - Call a method using a name with arguments in a list.
* return: error code
* obj(in): object
* name(in): method name
* returnval(out): return value container
* arglist(in): argument list
*
*/
int
obj_send_list (MOP obj, const char *name, DB_VALUE * returnval, DB_VALUE_LIST * arglist)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
bool class_method;
if ((obj == NULL) || (name == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = au_fetch_class (obj, &class_, AU_FETCH_READ, AU_EXECUTE);
if (error == NO_ERROR)
{
/* rare case when its ok to use this macro */
class_method = (IS_CLASS_MOP (obj)) ? true : false;
method = classobj_find_method (class_, name, class_method);
if (method == NULL)
{
ERROR1 (error, ER_OBJ_INVALID_METHOD, name);
}
else
{
error = obj_send_method_list (obj, class_, method, returnval, arglist);
}
}
}
return error;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/* obsolete, some tests use this, need to change them */
/*
* obj_send -
* return: error code
* obj(in): object
* name(in): method name
* returnval(out): return value container
* arglist(in): argument list
*
*/
int
obj_send (MOP obj, const char *name, DB_VALUE * returnval, DB_VALUE_LIST * arglist)
{
return (obj_send_list (obj, name, returnval, arglist));
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* obj_desc_send_list - Call a method using a descritor with arguments in a list
* return: error code
* obj(in): object
* desc(in): descriptor
* returnval(out): return value container
* arglist(in): argument list
*
*/
int
obj_desc_send_list (MOP obj, SM_DESCRIPTOR * desc, DB_VALUE * returnval, DB_VALUE_LIST * arglist)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
if ((obj == NULL) || (desc == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = sm_get_descriptor_component (obj, desc, 0, &class_, (SM_COMPONENT **) (&method));
if (error == NO_ERROR)
{
error = obj_send_method_list (obj, class_, method, returnval, arglist);
}
}
return error;
}
/*
* obj_send_method_array - This invokes a method where the arguments are
* supplied in an array of DB_VALUE pointers.
* Used by both obj_send_array() and
* obj_desc_send_array().
* return: error code
* obj(in): object to receive message
* class(in): class structure
* method(in): method structure
* returnval(out): return value container
* argarray(in): array of argument values
*
*/
static int
obj_send_method_array (MOP obj, SM_CLASS * class_, SM_METHOD * method, DB_VALUE * returnval, DB_VALUE ** argarray)
{
int error = NO_ERROR;
ARGSTATE state;
SM_METHOD_SIGNATURE *sig;
METHOD_FUNCTION func;
int expected;
func = method->function;
if (func == NULL)
{
error = sm_link_method (class_, method);
func = method->function;
}
if (func != NULL)
{
sig = method->signatures;
/*
* calculate the expected number of arguments
* allow the case where the arg count is set but there are no
* arg definitions, should be an error
*/
expected = ((sig != NULL && sig->num_args) ? sig->num_args : OBJ_MAX_ARGS);
/* get the arguments into the cannonical array */
argstate_from_array (&state, argarray);
/* need to handle this gracefully someday */
if (state.noverflow)
{
ERROR3 (error, ER_OBJ_TOO_MANY_ARGUMENTS, method->header.name, state.nargs + state.noverflow, OBJ_MAX_ARGS);
}
else
{
/*
* what happens when the actual count doesn't match the expected
* count and there is no domain definition ?
* for now, assume the supplied args are correct
*/
/* check argument domains if there are any */
if (sig != NULL && sig->args != NULL)
{
error = check_args (method, &state);
}
if (error == NO_ERROR)
{
error = call_method (func, obj, returnval, state.nargs, state.values, state.overflow);
}
}
cleanup_argstate (&state);
}
return (error);
}
/*
* obj_send_array - Call a method using a name with arguments in an array.
* return: error code
* obj(in): object
* name(in): method name
* returnval(out): return value container
* argarray(in): argument array
*
*/
int
obj_send_array (MOP obj, const char *name, DB_VALUE * returnval, DB_VALUE ** argarray)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
bool class_method;
if ((obj == NULL) || (name == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = au_fetch_class (obj, &class_, AU_FETCH_READ, AU_EXECUTE);
if (error == NO_ERROR)
{
/* rare case when its ok to use this macro */
class_method = (IS_CLASS_MOP (obj)) ? true : false;
method = classobj_find_method (class_, name, class_method);
if (method == NULL)
{
ERROR1 (error, ER_OBJ_INVALID_METHOD, name);
}
else
{
error = obj_send_method_array (obj, class_, method, returnval, argarray);
}
}
}
return error;
}
/*
* obj_desc_send_array - Call a method using a descritor with arguments in an array.
* return: error code
* obj(in): object
* desc(in): descriptor
* returnval(out): return value container
* argarray(in): argument array
*
*/
int
obj_desc_send_array (MOP obj, SM_DESCRIPTOR * desc, DB_VALUE * returnval, DB_VALUE ** argarray)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
if ((obj == NULL) || (desc == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = sm_get_descriptor_component (obj, desc, 0, &class_, (SM_COMPONENT **) (&method));
if (error == NO_ERROR)
{
error = obj_send_method_array (obj, class_, method, returnval, argarray);
}
}
return error;
}
/*
* obj_desc_send_array_quick - Call a method using a descritor
* with arguments in an array.
* return: error code
* obj(in): object
* desc(in): descriptor
* returnval(out): return value container
* nargs(in): number of arguments in array
* argarray(in): argument array
*
* Note:
* This is intended to be used by the parser to make repeated calls
* to methods when the arguments have already been validated/coerced.
* It simply gets the appropriate method from the descriptor and
* passes the argument array directly to call_method() without doing
* any type checking.
*/
int
obj_desc_send_array_quick (MOP obj, SM_DESCRIPTOR * desc, DB_VALUE * returnval, int nargs, DB_VALUE ** argarray)
{
int error = NO_ERROR;
SM_CLASS *class_;
SM_METHOD *method;
METHOD_FUNCTION func;
if ((obj == NULL) || (desc == NULL))
{
ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
}
else
{
error = sm_get_descriptor_component (obj, desc, 0, &class_, (SM_COMPONENT **) (&method));
if (error == NO_ERROR)
{
func = method->function;
if (func == NULL)
{
error = sm_link_method (class_, method);
func = method->function;
}
if (func != NULL)
{
/* need to handle this gracefully someday */
if (nargs > OBJ_MAX_ARGS)
{
ERROR3 (error, ER_OBJ_TOO_MANY_ARGUMENTS, method->header.name, nargs, OBJ_MAX_ARGS);
}
else
{
error = call_method (func, obj, returnval, nargs, argarray, NULL);
}
}
}
}
return error;
}
/*
* MISC OBJECT UTILITIES
*
*/
/*
* find_unique - Internal function called by the various flavors of functions
* that look for unique attribute values.
* return: object pointer
* classop(in):
* att(in): attrubute descriptor
* value(in): value to look for
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
* Note:
* This will try to find an object that has a particular value in either
* a unique btree or a regular query btree.
*
* If the attribute is not associated with any index or other optimized
* lookup structure, we will fabricate a select statement that attempts
* to locate the object.
*
* The end result is that this function should try hard to locate the
* object in the most effecient way possible, avoiding restrictive
* class locks where possible.
*
* If NULL is returned an error will be set. The error set if the
* object could not be found will be ER_OBJ_OBJECT_NOT_FOUND
*/
static MOP
find_unique (MOP classop, SM_ATTRIBUTE * att, DB_VALUE * value, AU_FETCHMODE fetchmode)
{
MOP found = NULL;
OID unique_oid;
BTID btid;
DB_TYPE value_type;
int r;
/* make sure all dirtied objects have been flushed */
if (!TM_TRAN_ASYNC_WS () && sm_flush_objects (classop) != NO_ERROR)
{
return NULL;
}
/*
* Check to see if we have any sort of index we can search, if not,
* then return an error indicating that the indexes do not exist rather than
* the "object not found" error.
*/
BTID_SET_NULL (&btid);
/* look for a unique index on this attribute */
r = classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_UNIQUE, &btid);
if (r == 0)
{
/* look for a primary key on this attribute */
r = classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_PRIMARY_KEY, &btid);
if (r == 0)
{
/* look for a reverse unique index on this attribute */
r = classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_REVERSE_UNIQUE, &btid);
if (r == 0)
{
/* couldn't find one, check for a index */
r = classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_INDEX, &btid);
if (r == 0)
{
/* couldn't find one, check for a reverse index */
r = classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_REVERSE_INDEX, &btid);
if (r == 0)
{
/* couldn't find anything to search in */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INDEX_NOT_FOUND, 0);
return NULL;
}
}
}
}
}
value_type = DB_VALUE_TYPE (value);
if (value_type == DB_TYPE_NULL)
{
/*
* We cannot search for a "null" value, though perhaps we could with some
* additional effort.
*/
goto notfound;
}
else if (value_type == DB_TYPE_OBJECT)
{
r = flush_temporary_OID (classop, value);
if (r == TEMPOID_FLUSH_NOT_SUPPORT)
{
goto notfound;
}
else if (r == TEMPOID_FLUSH_FAIL)
{
return NULL;
}
}
/* now search the index */
if (btree_find_unique (&btid, value, ws_oid (classop), &unique_oid) == BTREE_KEY_FOUND)
{
found = ws_mop (&unique_oid, NULL);
}
/*
* If we got an object, obtain an "S" lock before returning it, this
* avoid problems peeking at objects that were created
* by another transaction but which have not yet been committed.
* We may suspend here.
* Note that we're not getting an S lock on the class so we're still
* not technically correct in terms of the usual index scan locking
* model, but that's actually a desirable feature in this case.
*/
if (found != NULL)
{
/* Using LC_FETCH_DIRTY_VERSION instead current version, is a quick fix. Thus, we need to avoid fetching current
* version without any instance or shared/exclusive class lock, since btree_find_unique does not acquire locks in
* all cases. */
if (au_fetch_instance_force (found, NULL, fetchmode, LC_FETCH_DIRTY_VERSION) != NO_ERROR)
{
return NULL;
}
}
notfound:
/*
* since this is a common case, set this as a warning so we don't clutter
* up the error log.
*/
if (found == NULL && er_errid () != ER_LK_UNILATERALLY_ABORTED)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_OBJECT_NOT_FOUND, 0);
}
return found;
}
/*
* flush_temporary_OID -
* return: 0 : is not temporary OID or flushed
* 1 : don't support
* -1 : error occurs
*
* classop(in):
* key(in): OID value to look for
*
* Note:
* If the OID is temporary, we have something of a dilemma. It can't
* possibly be in the index since it's never been flushed to the server.
* That makes the index lookup useless, however, we could still have an
* object in the workspace with an attribute pointing to this object.
* The only reliable way to use the index is to first flush the class.
* This is what a SELECT statement would do anyway so its no big deal.
* If after flushing, the referenced object is still temporary, then it
* can't possibly be referenced by this class.
*/
static int
flush_temporary_OID (MOP classop, DB_VALUE * key)
{
MOP mop;
mop = db_get_object (key);
if (mop == NULL || WS_ISVID (mop))
{
/* if this is a virtual object, we don't support that */
return TEMPOID_FLUSH_NOT_SUPPORT;
}
else if (OID_ISTEMP (WS_OID (mop)))
{
/* flush this class and see if the value remains temporary */
if (!TM_TRAN_ASYNC_WS () && sm_flush_objects (classop) != NO_ERROR)
{
return TEMPOID_FLUSH_FAIL;
}
if (OID_ISTEMP (WS_OID (mop)))
{
return TEMPOID_FLUSH_NOT_SUPPORT;
}
}
return TEMPOID_FLUSH_OK;
}
/*
* obj_desc_find_unique -This is used to find the object which has a particular
* unique value.
* return: object which has the value if any
* op(in): class object
* desc(in): attribute descriptor
* value(in): value to look for
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
* Note:
* Calls find_unique to do the work after locating the proper
* internal attribute structure
*
*/
MOP
obj_desc_find_unique (MOP op, SM_DESCRIPTOR * desc, DB_VALUE * value, AU_FETCHMODE fetchmode)
{
SM_CLASS *class_;
SM_ATTRIBUTE *att;
MOP obj = NULL;
if (op == NULL || desc == NULL || value == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
}
else
{
/* map the descriptor into an actual pair of class/attribute structures */
if (sm_get_descriptor_component (op, desc, 0, &class_, (SM_COMPONENT **) (&att)) == NO_ERROR)
{
obj = find_unique (op, att, value, fetchmode);
}
}
return obj;
}
/*
* obj_find_unique - This is used to find the object which has a particular
* unique value.
* return: object which has the value if any
* op(in): class object
* attname(in): attribute name
* value(in): value to look for
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
* Note:
* Calls find_unique to do the work after locating the proper internal
* attribute structure.
*
*/
MOP
obj_find_unique (MOP op, const char *attname, DB_VALUE * value, AU_FETCHMODE fetchmode)
{
SM_CLASS *class_;
SM_ATTRIBUTE *att;
MOP obj;
obj = NULL;
if (op == NULL || attname == NULL || value == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
}
else if (au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT) == NO_ERROR)
{
att = classobj_find_attribute (class_, attname, 0);
if (att == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ATTRIBUTE, 1, attname);
}
else
{
obj = find_unique (op, att, value, fetchmode);
}
}
return obj;
}
/*
* obj_make_key_value
* return : object with the key value
*
* key(in):
* values(in):
* size(in):
*
*/
static DB_VALUE *
obj_make_key_value (DB_VALUE * key, const DB_VALUE * values[], int size)
{
int i, nullcnt;
DB_SEQ *mc_seq = NULL;
if (size == 1)
{
if (DB_IS_NULL (values[0]))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
*key = *values[0];
}
else
{
mc_seq = set_create_sequence (size);
if (mc_seq == NULL)
{
return NULL;
}
for (i = 0, nullcnt = 0; i < size; i++)
{
if (values[i] == NULL || set_put_element (mc_seq, i, (DB_VALUE *) values[i]) != NO_ERROR)
{
set_free (mc_seq);
return NULL;
}
if (DB_IS_NULL (values[i]))
{
nullcnt++;
}
}
if (nullcnt >= size)
{
set_free (mc_seq);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
db_make_sequence (key, mc_seq);
}
return key;
}
/*
* obj_find_multi_attr - This can be used to locate the instance whose key
* has a particular unique value
* return : object with the key value
* op(in): class pointer
* size(in): size of value array
* attr_names(in): array of attribute names
* values(in): array of value to look for
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
*/
MOP
obj_find_multi_attr (MOP op, int size, const char *attr_names[], const DB_VALUE * values[], AU_FETCHMODE fetchmode)
{
SM_CLASS *class_;
SM_CLASS_CONSTRAINT *cons;
MOP obj = NULL;
DB_VALUE key;
SM_ATTRIBUTE **attp;
const char **namep;
int i;
if (op == NULL || attr_names == NULL || values == NULL || size < 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
db_make_null (&key);
if (obj_make_key_value (&key, values, size) == NULL)
{
return NULL;
}
if (au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
{
goto end_find;
}
if (!TM_TRAN_ASYNC_WS () && sm_flush_objects (op) != NO_ERROR)
{
goto end_find;
}
for (cons = class_->constraints; cons; cons = cons->next)
{
if (SM_IS_CONSTRAINT_INDEX_FAMILY (cons->type))
{
attp = cons->attributes;
namep = attr_names;
if (!attp || !namep)
{
continue;
}
i = 0;
while (i < size && *attp && *namep && !SM_COMPARE_NAMES ((*attp)->header.name, *namep))
{
attp++;
namep++;
i++;
}
if (!*attp && i == size)
{
break;
}
}
}
if (cons == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
goto end_find;
}
obj = obj_find_object_by_cons_and_key (op, cons, &key, fetchmode);
end_find:
if (size > 1) /* must clear a multi-column index key */
pr_clear_value (&key);
return obj;
}
/*
* obj_find_multi_desc - This can be used to locate the instance whose key
* has a particular unique value.
*
* return: object with the key value
* op(in): class pointer
* size(in): size of value array
* desc(in): array of attribute descriptor
* values(in): array of value to look for
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
*/
MOP
obj_find_multi_desc (MOP op, int size, const SM_DESCRIPTOR * desc[], const DB_VALUE * values[], AU_FETCHMODE fetchmode)
{
SM_CLASS *class_;
SM_CLASS_CONSTRAINT *cons;
SM_ATTRIBUTE **attp;
SM_ATTRIBUTE **desc_comp = NULL;
SM_ATTRIBUTE **descp = NULL;
MOP obj = NULL;
DB_VALUE key;
int i;
size_t malloc_size;
if (op == NULL || desc == NULL || values == NULL || size < 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
db_make_null (&key);
if (obj_make_key_value (&key, values, size) == NULL)
{
return NULL;
}
if (au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
{
goto end_find;
}
if (!TM_TRAN_ASYNC_WS () && sm_flush_objects (op) != NO_ERROR)
{
goto end_find;
}
malloc_size = sizeof (SM_ATTRIBUTE *) * (size + 1);
desc_comp = (SM_ATTRIBUTE **) malloc (malloc_size);
if (desc_comp == NULL)
{
goto end_find;
}
for (i = 0; i < size; i++)
{
if (desc[i] == NULL
|| sm_get_descriptor_component (op, (SM_DESCRIPTOR *) desc[i], 0, &class_,
(SM_COMPONENT **) (&desc_comp[i])) != NO_ERROR)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
goto end_find;
}
}
desc_comp[size] = NULL;
for (cons = class_->constraints; cons; cons = cons->next)
{
if (SM_IS_CONSTRAINT_INDEX_FAMILY (cons->type))
{
attp = cons->attributes;
descp = desc_comp;
if (!attp || !descp)
{
continue;
}
i = 0;
while (i < size && *attp && *descp && (*attp)->id == (*descp)->id)
{
attp++;
descp++;
i++;
}
if (!*attp && !*descp)
{
break;
}
}
}
if (cons == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
goto end_find;
}
obj = obj_find_object_by_cons_and_key (op, cons, &key, fetchmode);
end_find:
if (desc_comp)
{
free_and_init (desc_comp);
}
if (size > 1) /* must clear a multi-column index key */
{
pr_clear_value (&key);
}
return obj;
}
/*
* obj_find_object_by_cons_and_key - Internal function called by the various
* flavors of functions that look for
* unique attribute values
* return: object pointer
* classop(in): class pointer
* cons(in): constraing
* key(in): key value
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
* Note:
* This will try to find an object that has a particular value in either
* a unique btree or a regular query btree.
*
* If the attribute is not associated with any index or other optimized
* lookup structure, we will fabricate a select statement that attempts
* to locate the object.
*
* The end result is that this function should try hard to locate the
* object in the most effecient way possible, avoiding restrictive
* class locks where possible.
*
* If NULL is returned an error will be set. The error set if the
* object could not be found will be ER_OBJ_OBJECT_NOT_FOUND
*/
static MOP
obj_find_object_by_cons_and_key (MOP classop, SM_CLASS_CONSTRAINT * cons, DB_VALUE * key, AU_FETCHMODE fetchmode)
{
DB_TYPE value_type;
int error;
DB_VALUE key_element;
DB_COLLECTION *keyset;
SM_ATTRIBUTE **att;
MOP obj;
OID unique_oid;
int r, i;
value_type = DB_VALUE_TYPE (key);
att = cons->attributes;
if (att == NULL || att[0]->domain == NULL)
{
return NULL;
}
obj = NULL;
error = ER_OBJ_OBJECT_NOT_FOUND;
if (value_type != DB_TYPE_SEQUENCE)
{
/* 1 column */
if (tp_domain_select (att[0]->domain, key, 1, TP_ANY_MATCH) == NULL)
{
error = ER_OBJ_INVALID_ARGUMENTS;
goto error_return;
}
if (value_type == DB_TYPE_OBJECT)
{
r = flush_temporary_OID (classop, key);
if (r == TEMPOID_FLUSH_NOT_SUPPORT)
{
error = ER_OBJ_INVALID_ARGUMENTS;
goto error_return;
}
else if (r == TEMPOID_FLUSH_FAIL)
{
return NULL;
}
}
}
else
{
/* multi column */
keyset = db_get_set (key);
if (keyset == NULL)
return NULL;
for (i = 0; att[i]; i++)
{
if (set_get_element (keyset, i, &key_element) != NO_ERROR)
{
error = ER_OBJ_INVALID_ARGUMENTS;
goto error_return;
}
value_type = DB_VALUE_TYPE (&key_element);
if (tp_domain_select (att[i]->domain, &key_element, 1, TP_ANY_MATCH) == NULL)
{
pr_clear_value (&key_element);
error = ER_OBJ_INVALID_ARGUMENTS;
goto error_return;
}
if (value_type == DB_TYPE_OBJECT)
{
r = flush_temporary_OID (classop, &key_element);
pr_clear_value (&key_element);
if (r == TEMPOID_FLUSH_NOT_SUPPORT)
{
error = ER_OBJ_INVALID_ARGUMENTS;
goto error_return;
}
else if (r == TEMPOID_FLUSH_FAIL)
{
return NULL;
}
}
}
}
if (btree_find_unique (&cons->index_btid, key, ws_oid (classop), &unique_oid) == BTREE_KEY_FOUND)
{
obj = ws_mop (&unique_oid, NULL);
/*
* If we got an object, obtain an "S" lock before returning it, this
* avoid problems peeking at objects that were created
* by another transaction but which have not yet been committed.
* We may suspend here.
* Note that we're not getting an S lock on the class so we're still
* not technically correct in terms of the usual index scan locking
* model, but that's actually a desirable feature in this case.
*/
if (obj != NULL)
{
/* Using LC_FETCH_DIRTY_VERSION instead current version, is a quick fix. Thus, we need to avoid fetching
* current version without any instance or shared/exclusive class lock, since btree_find_unique does not
* acquire locks in all cases. */
if (au_fetch_instance_force (obj, NULL, fetchmode, LC_FETCH_DIRTY_VERSION) != NO_ERROR)
{
return NULL;
}
}
}
error_return:
/*
* since this is a common case, set this as a warning so we don't clutter
* up the error log.
*/
if (obj == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
}
return obj;
}
/*
* obj_find_primary_key - This can be used to locate the instance whose
* primary key has a particular unique value.
* return: object with the primary key value
* op(in): class pointer
* values(in): list of value to look for
* size(in): size of value list
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
* Note:
* This will only work for class that have been defined
* with the PRIMARY KEY constraint
*/
MOP
obj_find_primary_key (MOP op, const DB_VALUE ** values, int size, AU_FETCHMODE fetchmode)
{
MOP obj = NULL;
int i;
DB_VALUE *key, tmp;
DB_SEQ *mc_seq = NULL;
if (op == NULL || values == NULL || size < 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
if (size == 1)
{
key = (DB_VALUE *) (values[0]);
}
else
{
mc_seq = set_create_sequence (size);
if (mc_seq == NULL)
{
goto notfound;
}
for (i = 0; i < size; i++)
{
if (set_put_element (mc_seq, i, (DB_VALUE *) (values[i])) != NO_ERROR)
{
goto notfound;
}
}
db_make_sequence (&tmp, mc_seq);
key = &tmp;
}
obj = obj_find_object_by_pkey (op, key, fetchmode);
notfound:
if (obj == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_OBJECT_NOT_FOUND, 0);
}
if (mc_seq)
{
set_free (mc_seq);
}
return obj;
}
/*
* obj_find_object_by_pkey - return the target object using the primary key.
* return: MOP
* classop(in): class pointer
* key(in): key value
* fetchmode(in): access type AU_FETCH_READ
* AU_FETCH_UPDATE
*
*/
MOP
obj_find_object_by_pkey (MOP classop, DB_VALUE * key, AU_FETCHMODE fetchmode)
{
SM_CLASS *class_;
SM_CLASS_CONSTRAINT *cons;
MOP obj;
OID unique_oid;
DB_TYPE value_type;
MOP mop;
BTREE_SEARCH btree_search;
assert (DB_VALUE_TYPE (key) != DB_TYPE_MIDXKEY);
obj = NULL;
if (classop == NULL || key == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return NULL;
}
if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
{
return NULL;
}
if (!TM_TRAN_ASYNC_WS () && sm_flush_objects (classop) != NO_ERROR)
{
return NULL;
}
cons = classobj_find_class_primary_key (class_);
if (cons == NULL)
{
goto notfound;
}
value_type = DB_VALUE_TYPE (key);
if (value_type == DB_TYPE_NULL)
{
/*
* We cannot search for a "null" value, though perhaps we could with some
* additional effort.
*/
goto notfound;
}
else if (value_type == DB_TYPE_OBJECT)
{
mop = db_get_object (key);
if (mop == NULL || WS_ISVID (mop))
{
/* if this is a virtual object, we don't support that */
goto notfound;
}
else if (OID_ISTEMP (WS_OID (mop)))
{
/* flush this class and see if the value remains temporary */
if (!TM_TRAN_ASYNC_WS () && sm_flush_objects (classop) != NO_ERROR)
{
return NULL;
}
if (OID_ISTEMP (WS_OID (mop)))
{
goto notfound;
}
}
}
btree_search = btree_find_unique (&cons->index_btid, key, ws_oid (classop), &unique_oid);
if (btree_search == BTREE_KEY_FOUND)
{
obj = ws_mop (&unique_oid, NULL);
}
/*
* If we got an object, obtain an "S" lock before returning it, this
* avoid problems peeking at objects that were created
* by another transaction but which have not yet been committed.
* We may suspend here.
* Note that we're not getting an S lock on the class so we're still
* not technically correct in terms of the usual index scan locking
* model, but that's actually a desirable feature in this case.
*/
if (obj != NULL)
{
/* Using LC_FETCH_DIRTY_VERSION instead current version, is a quick fix. Thus, we need to avoid fetching current
* version without any instance or shared/exclusive class lock, since btree_find_unique does not acquire locks in
* all cases. */
if (au_fetch_instance_force (obj, NULL, fetchmode, LC_FETCH_DIRTY_VERSION) != NO_ERROR)
{
return NULL;
}
}
notfound:
/*
* since this is a common case, set this as a warning so we don't clutter
* up the error log.
*/
if (obj == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_OBJECT_NOT_FOUND, 0);
}
return obj;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* obj_isclass - Tests to see if an object is a class object.
* return: non-zero if object is a class object
* obj(in): object
*
*/
int
obj_isclass (MOP obj)
{
int is_class = 0;
int status;
if (obj != NULL)
{
if (locator_is_class (obj, DB_FETCH_READ))
{
/* make sure it isn't deleted */
if (!WS_IS_DELETED (obj))
{
status = locator_does_exist_object (obj, DB_FETCH_READ);
if (status == LC_DOESNOT_EXIST)
{
WS_SET_DELETED (obj); /* remember this for later */
}
else if (status != LC_ERROR)
{
is_class = 1;
}
}
}
}
return (is_class);
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* obj_isinstance - Tests to see if an object is an instance object.
* return: non-zero if object is an instance
* obj(in): object
*
*/
int
obj_isinstance (MOP obj)
{
int is_instance = 0;
int status;
MOBJ object;
if (obj != NULL)
{
status = locator_is_class (obj, DB_FETCH_READ);
if (status < 0)
{
return status;
}
if (!status)
{
if (obj->is_temp)
{
is_instance = 1;
}
/*
* before declaring this an instance, we have to make sure it
* isn't deleted
*/
else if (!WS_IS_DELETED (obj))
{
if (WS_ISVID (obj))
{
if ((au_fetch_instance (obj, &object, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION (), AU_SELECT)) ==
NO_ERROR)
{
is_instance = 1;
}
}
else
{
status = locator_does_exist_object (obj, DB_FETCH_READ);
if (status == LC_DOESNOT_EXIST)
{
WS_SET_DELETED (obj); /* remember this for later */
}
else if (status != LC_ERROR)
{
is_instance = 1;
}
else
{
ASSERT_ERROR_AND_SET (is_instance);
}
}
}
}
}
return (is_instance);
}
/*
* obj_is_instance_of - Tests to see if an instance belongs to
* a particular class.
* return: 1 if object is an instance
* obj(in): instance
* class(in): class
*
* Note:
* Returns 0 if instance does not belong to class.
* Returns 1 if instance belongs to class.
* Returns -1 if error ocurred on instance access.
*/
int
obj_is_instance_of (MOP obj, MOP class_mop)
{
int status = 0;
MOP object_class_mop;
/*
* is it possible for the obj->class field to be unset and yet still have
* the class MOP in the workspace ?
*/
object_class_mop = ws_class_mop (obj);
if (object_class_mop == class_mop)
{
status = 1;
}
else
{
if (object_class_mop == NULL)
{
/* must force fetch of instance to get its class */
if (au_fetch_instance (obj, NULL, AU_FETCH_READ, TM_TRAN_READ_FETCH_VERSION (), AU_SELECT) != NO_ERROR)
{
status = -1;
}
else
{
if (object_class_mop == class_mop)
{
status = 1;
}
}
}
}
return (status);
}
/*
* obj_lock - Simplified interface for obtaining the basic read/write locks
* on an object.
* return: error code
* op(in): object to lock
* for_write(in): non-zero to get a write lock
* NOTE: This is a generic function can be used when a caller does not know
* whether op is an instance or a class mop.
*/
int
obj_lock (MOP op, int for_write)
{
int is_class = 0;
DB_FETCH_MODE class_purpose;
if (op->is_temp)
{
/* if it is a temporary object, just ignore the request */
return NO_ERROR;
}
class_purpose = ((for_write) ? DB_FETCH_CLREAD_INSTWRITE : DB_FETCH_CLREAD_INSTREAD);
is_class = locator_is_class (op, class_purpose);
if (is_class < 0)
{
return is_class;
}
if (is_class)
{
return obj_class_lock (op, for_write);
}
else
{
return obj_inst_lock (op, for_write);
}
}
/*
* obj_class_lock - Simplified interface for obtaining the basic read/write locks
* on a class object.
* return: error code
* op(in): object to lock
* for_write(in): non-zero to get a write lock
* NOTE: Callers should know op is a class mop.
*/
int
obj_class_lock (MOP op, int for_write)
{
int error = NO_ERROR;
if (op->is_temp)
{
/* if it is a temporary object, just ignore the request */
return NO_ERROR;
}
if (for_write)
{
error = au_fetch_class (op, NULL, AU_FETCH_UPDATE, AU_ALTER);
}
else
{
error = au_fetch_class (op, NULL, AU_FETCH_READ, AU_SELECT);
}
return error;
}
/*
* obj_inst_lock - Simplified interface for obtaining the basic read/write locks
* on an instance object.
* return: error code
* op(in): object to lock
* for_write(in): non-zero to get a write lock
* NOTE: Callers should know op is an instance mop.
*/
int
obj_inst_lock (MOP op, int for_write)
{
int error = NO_ERROR;
if (op->is_temp)
{
/* if it is a temporary object, just ignore the request */
return NO_ERROR;
}
if (for_write)
{
error = au_fetch_instance (op, NULL, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_UPDATE);
}
else
{
/* get dirty version, since need to lock the object */
error = au_fetch_instance (op, NULL, AU_FETCH_READ, LC_FETCH_DIRTY_VERSION, AU_SELECT);
}
return error;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* obj_find_unique_id - This function is used to identify attributes
* which posses a UNIQUE constraint and to
* return the associated B-tree IDs.
* return: error code
* op(in): class or instance
* att_name(in): attribute name
* id_array(in): array of BTID's where the results will be written
* id_array_size(in): size of the array (needed so that we don't overrun it)
* total_ids(in): total number of ids found (might be larger than
* id_array_size)
*
*/
int
obj_find_unique_id (MOP op, const char *att_name, BTID * id_array, int id_array_size, int *total_ids)
{
SM_CLASS *class_;
SM_ATTRIBUTE *att;
int error = NO_ERROR;
SM_CONSTRAINT *ptr;
int num_constraints = 0;
*total_ids = 0;
error = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
if (error == NO_ERROR)
{
att = classobj_find_attribute (class_, att_name, 0);
if (att == NULL)
{
error = ER_OBJ_INVALID_ATTRIBUTE;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, att_name);
}
else
{
for (ptr = att->constraints; ptr != NULL; ptr = ptr->next)
{
if (SM_IS_CONSTRAINT_UNIQUE_FAMILY (ptr->type))
{
if (num_constraints < id_array_size)
{
id_array[num_constraints] = ptr->index;
}
num_constraints++;
}
}
*total_ids = num_constraints;
}
}
return error;
}
#endif /* ENABLE_UNUSED_FUNCTION */