This is my first time trying to create an udf for mysql. The docs state that my_func_init gets called prior to executing the main function, yet in my environment this does not seem to happen.
long long charmatch(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
return 42;
}
my_bool charmatch_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
if (args->arg_count != 2)
{
strncpy(message, "charmatch() requires two arguments", 34);
return EXIT_FAILURE;
// was return EXIT_SUCCESS;
}
if(args->arg_type[0] != STRING_RESULT)
{
strncpy(message, "argument 1 must be a string", 27);
return EXIT_FAILURE;
// was return EXIT_SUCCESS;
}
if(args->arg_type[0] != STRING_RESULT)
{
strncpy(message, "argument 2 must be a string", 27);
return EXIT_FAILURE;
// was return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}
As you can see, I'm checking whether there are two arguments. In theory SELECT CHARMATCH() should return a message saying I must set two arguments, but it's not: it return 42.
Why doesn't it return the message?
EDIT Seems like the problem was in my return values for errors being 0 instead of 1. I fixed it and now calling SELECT CHARMATCH() with the wrong number of arguments returns an empty set. Why are the errors not being displayed?
You always return EXIT_SUCCESS in charmatch_init(), but
"The initialization function should return 0 if no error occurred and 1 otherwise. If an error occurs, xxx_init() should store a null-terminated error message in the message parameter. "
http://dev.mysql.com/doc/refman/5.0/en/udf-return-values.html
I guess that's why your error messages are ignored.
Related
I am trying to use my own UDF (written in c++) as a mysql extension. I was able to install the function via
CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf_func.so";
and use it. I could also drop the function via
DROP FUNCTION decrypt;
After that it would not be listed in the mysql.func table, so Im pretty sure it was successfully deleted.
Now I want to use an updated version of my function, and therefore reload it in mysql. However, mysql only reloads the old version of my file (which I completely removed from the computer). Even if I remove ALL files named "libudf_func.so" it will be reloaded.
I'm using mysql version 8.0.28 for macos12.0 on localhost.
How can I remove this cached version or force mysql to reload?
EDIT
All my steps, from loading the first version to my attempt to load the updated version:
I used: CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf.so"; to initially create the function decrypt
I changed something in my udf.cpp file.
I used: DROP FUNCTION decrypt; to remove the function decrypt
I used CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf.so"; as an attempt to reload/refresh the function decrypt (NO ERRORS)
the behaviors did not change (among other things I wanted to output a different error message, so that was pretty obviously the old behavior)
I renamed libudf.so to lubudf_func.so
I replicated this with the new name.
I deleted ALL files with the names libudf.so or lubudf_func.so from my machine
I could still execute CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf.so"; without any errors
I tried to call CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf_xyz.so"; (never existed), which failed. Error message: "Error Code: 1126. Can't open shared library 'libudf_xyz.so' (errno: 2 dlopen(/usr/local/opt/mysql/lib/plugin/libudf_xyz.so, 0x0002): tried: '/usr/local/opt/mysql/lib/plugin/libudf_xyz.so' (no such f)"
plugin_dir: /usr/local/opt/mysql/lib/plugin/
--->(this is where the .so file WAS located, but is not anymore)
How did I notice the old behavior was still present? The dead giveaway was that the function used the old error message ("Please use plaintext(int), iv(string), key(string)"), and not the new message ("Please use plaintext(string), iv(string), key(string)") after step no. 4
Also add the content of the plugins/ directory, which CREATE FUNCTION uses to load the function: I wrote the library in c++, then built it as a shared library. The c++ code would be as follows (but Im not sure this is relevant):
extern "C" {
bool decrypt_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
char *decrypt(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
void decrypt_deinit(UDF_INIT *initid);
}
bool decrypt_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
if (args->arg_count != 3 or
args->arg_type[0] != STRING_RESULT or
args->arg_type[1] != STRING_RESULT or
args->arg_type[2] != STRING_RESULT
) {
strcpy(message, "Usage: decrypt_poly(string ctext, string key, string iv)");
return 1;
}
if (args->lengths[2] != 48) {
strcpy(message, "Only 24-length IVs are allowed. Make sure IV is hexed.");
return 1;
}
initid->ptr = new char[args->lengths[0]];
initid->maybe_null = 1;
return 0;
}
char *decrypt(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error) {
std::string dvalue;
if (NULL != args->args[0]) {
std::string value = std::string(args->args[0], args->lengths[0]);
std::string key = std::string(args->args[1], args->lengths[1]);
std::string iv = std::string(args->args[2], args->lengths[2]);
try {
dvalue = ((encryption::poly_based_encryption::decrypt(value, key, iv))));
}
catch (CryptoPP::Exception &e) {
std::cerr << e.what() << std::endl;
*error = 1;
}
} else {
dvalue = "";
*is_null = 1;
*error = 1;
return NULL;
}
strcpy(initid->ptr, dvalue.c_str());
*length = dvalue.length();
result = initid->ptr;
*is_null = 0;
*error = 0;
return result;
}
void decrypt_deinit(UDF_INIT *initid) {
if (initid->ptr)
delete[] initid->ptr;
}
I have a function that returns int value but in some circumstances I want to terminate the function and return nothing.
Just to show an example:
int numberFunc(int num){
if(num > 10){
return;
/* terminates the function for numbers more than 10 */
}
return num;
}
This works fine but I get a warning that says the function has a return type of int but it doesn’t finish with a return statement.
Will there be a problem if I use something like this and what can be a solution for that?
Thanks
Solution:
As julemand101 explained, We can return a null value. Also the code above returns a null value for numbers more than 10 so we have to take care of the possible null values later on.
What you properly want is to return null to indicate that the method are not returning any value:
int numberFunc(int num){
if(num > 10){
return null;
/* terminates the function for numbers more than 10 */
}
return num;
}
But remember that the method which are using the result from numberFunc needs to be aware that the returned value can be null.
You can just return null. It's a default value for any type that has not been set yet.
Ex.:
int i; is the same as int i = null
So I'm updating this as I've gotten JSON OUT however the JSON is followed by an INT
{"function":"timereport","values":545028}41
#include <ArduinoJson.h>
char owner[] = "";
const int capacity = JSON_OBJECT_SIZE(2);
DynamicJsonDocument doc(capacity);
void setup() {
Serial.begin(115200);
}
void loop() {
doc["function"]="timereport";
doc["values"]=millis();
Serial.println(serializeJson(doc, Serial));
delay(5000);
}
Second TRY:(Post Still has the INT at the end)
char json_string[256];
http.begin("http://192.168.1.103:2000/hydroapi"); //Specify destination for HTTP request
http.addHeader("Content-Type","application/json");
doc["function"]="timereport";
doc["values"]=millis();
serializeJson(doc, json_string);
int httpResponseCode = http.POST(json_string); //Send the actual POST request
http.end();
The call to serializeJson prints the json to Serial (as that's the stream your passing in) and returns the number of bytes printed, which you're then explicitly printing using Serial.println.
Remove the call to Serial.println to avoid printing the number of bytes, just call serializeJson(doc, Serial);
Below is the sample code I'm using to understand exception handling in completablefuture in java8.
If we make use of exceptionally method as per doc,
exceptionally method catches even runtime exception as well and proceeds to last block in the pipeline.
if we don't use exceptionally method then, it justs prints running and exits.
Correct me if my understanding isn't correct.
Question is Lets say if i want to throw runtime exception and want application to stop. Basically if i throw Runtime exception , it shouldn't proceed to next block in pipeline. How should i do that. Any pointers are helpful.
public static void main(String[] args) {
final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
System.out.println("running");
int i = 0;
if(i == 0) {
throw new RuntimeException("ding");
}
return "test";
}).exceptionally(it -> {
System.out.println(it.getMessage());
return "empty";
}).thenApply(it -> {
System.out.println("last block" + it);
return "dummy";
});
}
Try this:
public static void main(String[] args) {
try {
final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
System.out.println("running");
int i = 0;
if (i == 0) {
throw new RuntimeException("ding");
}
return "test";
}).exceptionally(it -> {
if (it.getMessage().contains("ding")) {
throw (RuntimeException) it;
}
System.out.println(it.getMessage());
return "empty";
}).thenApply(it -> {
System.out.println("last block" + it);
return "dummy";
});
retrieveName.join();
} catch (Exception e) {
System.out.println("main() exception, cause=" + e.getCause());
}
}
This is the output:
running
main() exception, cause=java.lang.RuntimeException: ding
I made 3 small changes to your code:
Wrapped it all in a try-catch
Threw a RuntimeException in exceptionally() for the "ding" exception.
Added a call to retrieveName.join(). From the Javadoc for CompletableFuture.join():
public T join()
Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally.
Update based on OP feedback ------->
Lets say if i want to throw runtime exception and want application to
stop. Basically if i throw Runtime exception , it shouldn't proceed to
next block in pipeline. How should i do that.
You can achieve what you want with just 2 changes to your code:
[1] Completely remove the exceptionally() callback so the CompletableFuture (CF) terminates with an exception. In exceptionally() in the OP code the exception was being swallowed rather than rethrown, and returning a CF, so the thenApply() method was still performed.
[2] Add a call to retrieveName.join() at the end of main(). This is a blocking call, but since the thread had terminated with an exception that 's not really relevant for the sample code. The join() method will extract the thrown RunTimeException and re-throw it, wrapped in a CompletionException.
Here's your modified code:
public static void main(String[] args) {
final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
System.out.println("running");
int i = 0;
if(i == 0) {
throw new RuntimeException("ding");
}
return "test";
}).thenApply(it -> {
System.out.println("last block" + it);
return "dummy";
});
retrieveName.join();
}
Notes:
This is not how to do things in Production. The blocking call from join() was not a problem here, but could be for a long running CF. But you obviously can't extract the exception from the CF until it is complete, so it makes sense that the join() call blocks.
Always bear in mind that main() is not running in the same thread(s) as the CF.
An alternative approach (if viable) might be to handle all the necessary post-exception actions (logging, etc,) within exceptionally() and then terminate normally with a suitable return value (e.g. "Exception handled!") rather than propagating the exception.
You can check whether the CF is still running by calling the non-blocking isDone() method. You can also check whether the CF ended with an exception (isCompletedExceptionally()) or was cancelled(isCancelled()).
I've got a problem with using boost::bind and boost::function and passing boost::function as a callback into another class.
Here's an example that is the problematic situation:
typedef boost::function<void (bool)> callbackFunction;
class HasCallback
{
public:
HasCallback() : value(0)
{
}
int value;
void CallBackFunction(bool changed)
{
std::cout << "HasCallback class. CallBackFunction called. Parameter: " << value << std::endl;
}
};
class ReceivesCallback
{
public:
void AddCallback(callbackFunction newFunc)
{
callbacks.push_back(newFunc);
}
void execute(int &i)
{
for(std::vector<callbackFunction>::iterator it = callbacks.begin(); it != callbacks.end(); it++)
{
(*it)(i++);
}
}
void RemoveHandler(callbackFunction oldFunc)
{
for(std::vector<callbackFunction>::iterator it = callbacks.begin(); it != callbacks.end(); it++)
{
if((*it) == oldFunc)
{
callbacks.erase(it);
break;
}
}
}
private:
std::vector<callbackFunction> callbacks;
};
int main()
{
HasCallback hc;
ReceivesCallback rc;
rc.AddCallback(boost::bind(&HasCallback::CallBackFunction, &hc, _1));
hc.value = 123;
HasCallback hc2;
rc.AddCallback(boost::bind(&HasCallback::CallBackFunction, &hc2, _1));
hc2.value = 321;
int a = 0;
rc.RemoveHandler(boost::bind(&HasCallback::CallBackFunction, &hc2, _1));
rc.execute(a);
}
The problem I'm having is that this doesn't even compile. It fails within ReceivesCallback::RemoveHandler in the if((*it) == oldFunc) line with the error saying that there's more than one overload of the operator== for the thing i'm trying to do.
I keep searching for this and can't find what I'm doing wrong. Also, I keep finding contradicting information, one saying that it's possible to compare boost::function-s and another saying it's not. I can see the operator== functions within boost/function_base.hpp and i believe this is supposed to work, I just can't seem to figure out how. Can someone help me out here? My suspicion is that it fails because the parameters of the boost::bind need to be specified fully(be concrete values) but this is something i cannot get in the code I'm developing, I just need to know whether the passed handler is registered or not, since I'm binding to an object it should have all the information neeeded to make the distinction.
See Boost.Function FAQ for an explanation : Why can't I compare boost::function objects with operator== or operator!= ?.
Boost.Functions only provides comparison of a boost::function with an arbitrary function object. I believe that making your RemoveHandler member function template could fix the issue :
template<class Functor>
void RemoveHandler(const Functor &oldFunc)
{
for(std::vector<callbackFunction>::iterator it = callbacks.begin(); it != callbacks.end(); it++)
{
if((*it) == oldFunc)
{
callbacks.erase(it);
break;
}
}
}
Here, oldFunc gets to keep its actual type without being 'wrapped' in a boost::function.