File query_method.cpp¶
File List > cubrid > src > method > query_method.cpp
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.
*
*/
/*
* query_method.c - Method calls in queries
*/
#ident "$Id$"
#include "query_method.hpp"
#include <unordered_map>
#include <vector>
#include "dbtype.h"
#include "method_struct_invoke.hpp"
#if !defined (SERVER_MODE)
#include "authenticate.h" /* AU_ENABLE, AU_DISABLE */
#include "dbi.h" /* db_enable_modification(), db_disable_modification() */
#include "object_accessor.h" /* obj_ */
#include "object_primitive.h" /* pr_is_set_type() */
#include "set_object.h" /* set_convert_oids_to_objects() */
#include "virtual_object.h" /* vid_oid_to_object() */
#include "network.h"
#include "network_interface_cl.h"
#include "mem_block.hpp" /* cubmem::extensible_block */
#include "method_callback.hpp"
#include "method_query_handler.hpp"
#include "transaction_cl.h"
#include "packer.hpp" /* packing_packer */
#include "network_callback_cl.hpp"
#endif
#if defined (SERVER_MODE) || defined (SA_MODE)
#include "method_invoke_group.hpp"
#include "thread_compat.hpp"
#endif
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#if defined (SA_MODE)
int method_Num_method_jsp_calls = 0;
extern unsigned int db_on_server;
#define ENTER_SERVER_IN_METHOD_CALL(save_pri_heap_id_) \
do { \
db_on_server = 1; \
private_heap_id = save_pri_heap_id_; \
} while (0)
#define EXIT_SERVER_IN_METHOD_CALL(save_pri_heap_id_) \
do { \
save_pri_heap_id_ = private_heap_id; \
private_heap_id = 0; \
db_on_server = 0; \
} while (0)
#endif
#if !defined (SERVER_MODE)
/* For builtin C Method */
static std::unordered_map <UINT64, std::vector<DB_VALUE>> runtime_args;
/* data queue */
static std::queue <cubmem::extensible_block> data_queue;
static void method_erase_runtime_arguments (UINT64 id);
static void method_set_runtime_arguments (UINT64 id, std::vector<DB_VALUE> &args);
static int method_prepare_arguments (packing_unpacker &unpacker);
static int method_invoke_builtin (packing_unpacker &unpacker, DB_VALUE &result);
static int method_invoke_builtin_internal (DB_VALUE &result, std::vector<DB_VALUE> &args, cubpl::pl_signature &sig);
static int method_dispatch_internal (packing_unpacker &unpacker);
/* FIXME: duplicated function implementation; The following three functions are ported from the client cursor (cursor.c) */
static bool method_has_set_vobjs (DB_SET *set);
static int method_fixup_set_vobjs (DB_VALUE *value_p);
static int method_fixup_vobjs (DB_VALUE *value_p);
#endif
#if defined (CS_MODE)
/*
* method_dispatch () - Dispatch method protocol from the server
* return:
* rc(in) : enquiry return code
* host(in) : host name
* server_name(in) : server name
* methoddata (in) : data buffer
* methoddata_size (in) : data buffer size
*/
int
method_dispatch (unsigned int rc, char *methoddata, int methoddata_size)
{
int error = NO_ERROR;
packing_unpacker unpacker (methoddata, (size_t) methoddata_size);
tran_begin_libcas_function ();
int depth = tran_get_libcas_depth ();
if (depth > METHOD_MAX_RECURSION_DEPTH)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_TOO_MANY_NESTED_CALL, 0);
error = ER_SP_TOO_MANY_NESTED_CALL;
}
if (error == NO_ERROR)
{
xs_set_conn_info (depth - 1, rc);
error = method_dispatch_internal (unpacker);
}
tran_end_libcas_function ();
return error;
}
/*
* method_error () - Send error code to the server
* return:
* rc(in) : enquiry return code
* host(in) : host name
* server_name(in) : server name
* error_id (in) : error code
*/
int
method_error (unsigned int rc, int error_id)
{
int error = NO_ERROR;
tran_begin_libcas_function();
int depth = tran_get_libcas_depth ();
xs_set_conn_info (depth - 1, rc);
error = xs_send_queue (METHOD_ERROR, error_id);
tran_end_libcas_function();
return error;
}
#elif defined (SA_MODE)
/*
* method_dispatch () - Dispatch method protocol for SA Mode
* return:
* unpacker(in) : unpacker for request
*/
int
method_dispatch (packing_unpacker &unpacker)
{
int error = NO_ERROR;
HL_HEAPID save_pri_heap_id;
EXIT_SERVER_IN_METHOD_CALL (save_pri_heap_id);
++method_Num_method_jsp_calls;
tran_begin_libcas_function ();
int depth = tran_get_libcas_depth ();
if (depth > METHOD_MAX_RECURSION_DEPTH)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_TOO_MANY_NESTED_CALL, 0);
error = ER_SP_TOO_MANY_NESTED_CALL;
}
if (error == NO_ERROR)
{
error = method_dispatch_internal (unpacker);
}
tran_end_libcas_function ();
--method_Num_method_jsp_calls;
ENTER_SERVER_IN_METHOD_CALL (save_pri_heap_id);
return error;
}
#endif
#if !defined (SERVER_MODE)
/*
* method_dispatch_internal () - Dispatch method protocol from the server
* return:
* rc(in) : enquiry return code
* host(in) : host name
* server_name(in) : server name
* methoddata (in) : data buffer
* methoddata_size (in) : data buffer size
*/
int
method_dispatch_internal (packing_unpacker &unpacker)
{
int error = NO_ERROR;
cubmethod::header header (unpacker);
if (error == NO_ERROR)
{
int save_auth = 0;
switch (header.command)
{
case METHOD_REQUEST_ARG_PREPARE:
error = method_prepare_arguments (unpacker);
break;
case METHOD_REQUEST_INVOKE:
AU_SAVE_AND_ENABLE (save_auth);
DB_VALUE value;
error = method_invoke_builtin (unpacker, value);
AU_RESTORE (save_auth);
break;
case METHOD_REQUEST_CALLBACK:
AU_SAVE_AND_ENABLE (save_auth);
error = cubmethod::get_callback_handler()->callback_dispatch (unpacker);
AU_RESTORE (save_auth);
break;
case METHOD_REQUEST_END:
{
std::vector <int> handlers;
unpacker.unpack_all (handlers);
for (size_t i = 0; i < handlers.size (); i++)
{
cubmethod::get_callback_handler()->free_query_handle (handlers[i], false);
}
}
break;
default:
assert (false); // the other callbacks are disabled now
return ER_FAILED;
break;
}
}
return error;
}
/*
* method_invoke_builtin () - Invoke C Method with runtime arguments
* return:
* unpacker (in) : unpacker
* result (out) : result
*/
static int
method_invoke_builtin (packing_unpacker &unpacker, DB_VALUE &result)
{
int error = NO_ERROR;
uint64_t group_id;
cubpl::pl_signature ib;
unpacker.unpack_all (group_id, ib);
auto search = runtime_args.find (group_id);
if (search != runtime_args.end())
{
std::vector<DB_VALUE> &args = search->second;
error = method_invoke_builtin_internal (result, args, ib);
if (error == NO_ERROR)
{
/* send a result value to server */
error = xs_send_queue (METHOD_SUCCESS, result);
}
}
else
{
error = ER_GENERIC_ERROR;
}
return error;
}
/*
* method_prepare_arguments () - Stores at DB_VALUE arguments (runtime_args) for C Method
* return:
* unpacker (in) : unpacker
* conn_info (in) : enquiry return code, host name, server name
*/
static int
method_prepare_arguments (packing_unpacker &unpacker)
{
UINT64 id;
std::vector<DB_VALUE> arguments;
unpacker.unpack_all (id, arguments);
method_erase_runtime_arguments (id);
method_set_runtime_arguments (id, arguments);
return NO_ERROR;
}
/*
* method_erase_runtime_arguments () -
* return: void
* id (in) : method_invoke_group's id
*/
void
method_erase_runtime_arguments (UINT64 id)
{
auto search = runtime_args.find (id);
if (search != runtime_args.end())
{
std::vector<DB_VALUE> &prev_args = search->second;
pr_clear_value_vector (prev_args);
runtime_args.erase (search);
}
}
/*
* method_set_runtime_arguments () -
* return: void
* id (in) : method_invoke_group's id
* args (in) : DB_VALUE arguments
*/
void
method_set_runtime_arguments (UINT64 id, std::vector<DB_VALUE> &args)
{
for (DB_VALUE &v : args)
{
method_fixup_vobjs (&v);
}
runtime_args.insert ({id, args});
}
/*
* method_invoke_builtin_internal () -
* return: int
* result (out) :
* args (in) : objects & arguments DB_VALUEs
* meth_sig_p (in) : Method signatures
*/
// *INDENT-OFF*
int
method_invoke_builtin_internal (DB_VALUE & result, std::vector<DB_VALUE> &args, cubpl::pl_signature &sig)
// *INDENT-ON*
{
int error = NO_ERROR;
int turn_on_auth = 1;
/* The first position # is for the object ID */
int num_args = sig.arg.arg_size + 1;
// *INDENT-OFF*
std::vector <DB_VALUE *> arg_val_p (num_args + 1, NULL);
// *INDENT-ON*
for (int i = 0; i < num_args; ++i)
{
int pos = sig.ext.method.arg_pos[i];
arg_val_p[i] = &args[pos];
}
db_make_null (&result);
/* Don't call the method if the object is NULL or it has been deleted. A method call on a NULL object is
* NULL. */
if (!DB_IS_NULL (arg_val_p[0]))
{
error = db_is_any_class (db_get_object (arg_val_p[0]));
if (error == 0)
{
error = db_is_instance (db_get_object (arg_val_p[0]));
}
}
if (error == ER_HEAP_UNKNOWN_OBJECT)
{
error = NO_ERROR;
}
else if (error > 0)
{
/* methods must run with authorization turned on and database modifications turned off. */
turn_on_auth = 0;
AU_ENABLE (turn_on_auth);
db_disable_modification ();
error = obj_send_array (db_get_object (arg_val_p[0]), sig.name, &result, &arg_val_p[1]);
db_enable_modification ();
AU_DISABLE (turn_on_auth);
}
/* error handling */
if (error != NO_ERROR)
{
pr_clear_value (&result);
error = ER_FAILED;
}
else if (DB_VALUE_TYPE (&result) == DB_TYPE_ERROR)
{
if (er_errid () == NO_ERROR) /* caller has not set an error */
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 1);
}
pr_clear_value (&result);
error = ER_GENERIC_ERROR;
}
return error;
}
/*
* method_has_set_vobjs () -
* return: nonzero iff set has some vobjs, zero otherwise
* set (in): set/sequence db_value
*/
static bool
method_has_set_vobjs (DB_SET *set)
{
int i, size;
DB_VALUE element;
size = db_set_size (set);
for (i = 0; i < size; i++)
{
if (db_set_get (set, i, &element) != NO_ERROR)
{
return false;
}
if (DB_VALUE_TYPE (&element) == DB_TYPE_VOBJ)
{
pr_clear_value (&element);
return true;
}
pr_clear_value (&element);
}
return false;
}
/*
* method_fixup_set_vobjs() - if val is a set/seq of vobjs then
* turn it into a set/seq of vmops
* return: NO_ERROR on all ok, ER status( or ER_FAILED) otherwise
* value_p (in/out): a db_value
*/
static int
method_fixup_set_vobjs (DB_VALUE *value_p)
{
DB_TYPE type;
int rc, i, size;
DB_VALUE element;
DB_SET *set, *new_set;
type = DB_VALUE_TYPE (value_p);
if (!pr_is_set_type (type))
{
return ER_FAILED;
}
set = db_get_set (value_p);
size = db_set_size (set);
if (method_has_set_vobjs (set) == false)
{
return set_convert_oids_to_objects (set);
}
switch (type)
{
case DB_TYPE_SET:
new_set = db_set_create_basic (NULL, NULL);
break;
case DB_TYPE_MULTISET:
new_set = db_set_create_multi (NULL, NULL);
break;
case DB_TYPE_SEQUENCE:
new_set = db_seq_create (NULL, NULL, size);
break;
default:
return ER_FAILED;
}
/* fixup element vobjs into vmops and add them to new */
for (i = 0; i < size; i++)
{
if (db_set_get (set, i, &element) != NO_ERROR)
{
db_set_free (new_set);
return ER_FAILED;
}
if (method_fixup_vobjs (&element) != NO_ERROR)
{
db_set_free (new_set);
return ER_FAILED;
}
if (type == DB_TYPE_SEQUENCE)
{
rc = db_seq_put (new_set, i, &element);
}
else
{
rc = db_set_add (new_set, &element);
}
if (rc != NO_ERROR)
{
db_set_free (new_set);
return ER_FAILED;
}
}
pr_clear_value (value_p);
switch (type)
{
case DB_TYPE_SET:
db_make_set (value_p, new_set);
break;
case DB_TYPE_MULTISET:
db_make_multiset (value_p, new_set);
break;
case DB_TYPE_SEQUENCE:
db_make_sequence (value_p, new_set);
break;
default:
db_set_free (new_set);
return ER_FAILED;
}
return NO_ERROR;
}
/*
* method_fixup_vobjs () -
* return: NO_ERROR on all ok, ER status( or ER_FAILED) otherwise
* value_p (in/out): a db_value
* Note: if value_p is an OID then turn it into an OBJECT type value
* if value_p is a VOBJ then turn it into a vmop
* if value_p is a set/seq then do same fixups on its elements
*/
static int
method_fixup_vobjs (DB_VALUE *value_p)
{
DB_OBJECT *obj;
int rc;
switch (DB_VALUE_DOMAIN_TYPE (value_p))
{
case DB_TYPE_OID:
rc = vid_oid_to_object (value_p, &obj);
db_make_object (value_p, obj);
break;
case DB_TYPE_VOBJ:
if (DB_IS_NULL (value_p))
{
pr_clear_value (value_p);
db_value_domain_init (value_p, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
rc = NO_ERROR;
}
else
{
rc = vid_vobj_to_object (value_p, &obj);
pr_clear_value (value_p);
db_make_object (value_p, obj);
}
break;
case DB_TYPE_SET:
case DB_TYPE_MULTISET:
case DB_TYPE_SEQUENCE:
// fixup any set/seq of vobjs into a set/seq of vmops
rc = method_fixup_set_vobjs (value_p);
value_p->need_clear = true;
break;
default:
rc = NO_ERROR;
break;
}
return rc;
}
#endif
#if defined (SERVER_MODE) || defined (SA_MODE)
#if 0
/*
* xmethod_invoke_fold_constants () - perform constant folding for method
* return : error code
* thread_p (in) : worker thread
* sig_list(in) : method signature
* args(in) : method argument
*/
int xmethod_invoke_fold_constants (THREAD_ENTRY *thread_p, const method_sig_list &sig_list,
std::vector<std::reference_wrapper<DB_VALUE>> &args,
DB_VALUE &result)
{
int error_code = NO_ERROR;
cubmethod::method_invoke_group *method_group = cubmethod::get_rctx (thread_p)->create_invoke_group (thread_p, sig_list,
false);
method_group->begin ();
std::vector<bool> dummy_use_vec (args.size(), true);
error_code = method_group->prepare (args, dummy_use_vec);
if (error_code != NO_ERROR)
{
return error_code;
}
error_code = method_group->execute (args);
if (error_code != NO_ERROR)
{
return error_code;
}
DB_VALUE &res = method_group->get_return_value (0);
db_value_clone (&res, &result);
return error_code;
}
#endif
#endif