00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021 #include <stdio.h>
00022
00023 #include <drizzled/error.h>
00024 #include <drizzled/plugin/function.h>
00025 #include <drizzled/function/str/strfunc.h>
00026 #include <drizzled/temporal.h>
00027
00028 #include <v8.h>
00029 #define JS_ENGINE "v8"
00030
00031 using namespace std;
00032 using namespace drizzled;
00033
00034
00035 namespace drizzle_plugin {
00036 namespace js {
00037
00038 v8::Handle<v8::Value> V8Version(const v8::Arguments& args);
00039 v8::Handle<v8::Value> JsEngine(const v8::Arguments& args);
00040 const char* v8_to_char(const v8::String::Utf8Value& value);
00041 void emit_drizzle_error(v8::TryCatch* try_catch);
00042
00043
00044
00045
00046
00047
00048 class JsFunction : public Item_str_func
00049 {
00050 public:
00051 String *val_str(String *);
00052
00053 const char *func_name() const
00054 {
00055 return "js";
00056 }
00057
00058 void fix_length_and_dec()
00059 {
00060 maybe_null= 1;
00061 max_length= MAX_BLOB_WIDTH;
00062 }
00063
00064 bool check_argument_count(int n)
00065 {
00066 return (n >= 1);
00067 }
00068 };
00069
00076 const char* v8_to_char(const v8::String::Utf8Value& value) {
00077 return *value ? *value : "<javascript v8 string conversion failed>";
00078 }
00079
00085 void emit_drizzle_error(v8::TryCatch* try_catch)
00086 {
00087 v8::HandleScope handle_scope;
00088 v8::String::Utf8Value exception(try_catch->Exception());
00089 const char* exception_string = v8_to_char(exception);
00090 v8::Handle<v8::Message> message = try_catch->Message();
00091 if (message.IsEmpty()) {
00092
00093
00094 my_error(ER_SCRIPT, MYF(0), exception_string);
00095 } else {
00096 char buf[2048];
00097 int linenum = message->GetLineNumber();
00098 sprintf(buf, "At line %i: %.1900s (Do SHOW ERRORS for more information.)", linenum, exception_string);
00099 my_error(ER_SCRIPT, MYF(0), buf);
00100
00101 v8::String::Utf8Value sourceline(message->GetSourceLine());
00102 const char* sourceline_string = v8_to_char(sourceline);
00103 sprintf(buf, "Line %i: %.160s", linenum, sourceline_string);
00104 my_error(ER_SCRIPT, MYF(0), buf);
00105 int start = message->GetStartColumn();
00106 sprintf(buf, "Check your script starting at: '%.50s'", &sourceline_string[start]);
00107 my_error(ER_SCRIPT, MYF(0), buf);
00108 v8::String::Utf8Value stack_trace(try_catch->StackTrace());
00109 if (stack_trace.length() > 0) {
00110 const char* stack_trace_string = v8_to_char(stack_trace);
00111 my_error(ER_SCRIPT, MYF(0), stack_trace_string);
00112 }
00113 }
00114 }
00115
00139 String *JsFunction::val_str( String *str )
00140 {
00141 assert( fixed == 1 );
00142
00143
00144 null_value= true;
00145
00146 String *source_str=NULL;
00147 source_str = args[0]->val_str( str );
00148
00149
00150
00151
00152
00153
00154 v8::Locker locker;
00155
00156 v8::HandleScope handle_scope;
00157
00158 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
00159
00160 v8::Handle<v8::ObjectTemplate> db = v8::ObjectTemplate::New();
00161 v8::Handle<v8::ObjectTemplate> js = v8::ObjectTemplate::New();
00162
00163 global->Set( v8::String::New("db"), db );
00164 db->Set( v8::String::New("js"), js );
00165 js->Set( v8::String::New("version"), v8::FunctionTemplate::New(V8Version) );
00166 js->Set( v8::String::New("engine"), v8::FunctionTemplate::New(JsEngine) );
00167
00168
00169
00170 v8::Persistent<v8::Context> context = v8::Context::New( NULL, global );
00171 if ( context.IsEmpty() ) {
00172 char buf[100];
00173 sprintf(buf, "Error in js() while creating JavaScript context in %s.", JS_ENGINE);
00174 my_error(ER_SCRIPT, MYF(0), buf);
00175 return NULL;
00176 }
00177 context->Enter();
00178
00179 v8::Handle<v8::Array> a = v8::Array::New(arg_count-1);
00180 for( uint64_t n = 1; n < arg_count; n++ )
00181 {
00182
00183
00184 if( args[n]->result_type() == INT_RESULT ){
00185
00186 if( args[n]->is_unsigned() ) {
00187 a->Set( n-1, v8::Integer::NewFromUnsigned( (uint32_t) args[n]->val_uint() ) );
00188 } else {
00189 a->Set( n-1, v8::Integer::New((int32_t)args[n]->val_int() ) );
00190 }
00191 } else if ( args[n]->result_type() == REAL_RESULT || args[n]->result_type() == DECIMAL_RESULT ) {
00192 a->Set( n-1, v8::Number::New(args[n]->val_real() ) );
00193 } else if ( true || args[n]->result_type() == STRING_RESULT ) {
00194 if ( args[n]->is_datetime() ) {
00195
00196
00197
00198 type::Time ltime;
00199 Timestamp temporal;
00200 args[n]->get_date(ltime, 0);
00201 temporal.set_years(ltime.year);
00202 temporal.set_months(ltime.month);
00203 temporal.set_days(ltime.day);
00204 temporal.set_hours(ltime.hour);
00205 temporal.set_minutes(ltime.minute);
00206 temporal.set_seconds(ltime.second);
00207 temporal.set_epoch_seconds();
00208 if (temporal.is_valid())
00209 {
00210 time_t tmp;
00211 temporal.to_time_t(tmp);
00212
00213
00214 a->Set( n-1, v8::Date::New(((uint64_t)tmp)*1000) );
00215 } else {
00216 a->Set( n-1, v8::String::New(args[n]->val_str(str)->c_str() ) );
00217 }
00218 } else {
00219
00220 a->Set( n-1, v8::String::New(args[n]->val_str(str)->c_str() ) );
00221 }
00222 }
00223
00224 if( ! args[n]->is_autogenerated_name ) {
00225 if( args[n]->result_type() == INT_RESULT ){
00226 if( args[n]->is_unsigned() ) {
00227 context->Global()->Set( v8::String::New( args[n]->name ), v8::Integer::NewFromUnsigned( (uint32_t) args[n]->val_uint() ) );
00228 } else {
00229 context->Global()->Set( v8::String::New( args[n]->name ), v8::Integer::New((int32_t)args[n]->val_int() ) );
00230 }
00231 } else if ( args[n]->result_type() == REAL_RESULT || args[n]->result_type() == DECIMAL_RESULT ) {
00232 context->Global()->Set( v8::String::New( args[n]->name ), v8::Number::New(args[n]->val_real() ) );
00233 } else if ( true || args[n]->result_type() == STRING_RESULT ) {
00234 if ( args[n]->is_datetime() ) {
00235
00236
00237
00238 type::Time ltime;
00239 Timestamp temporal;
00240 args[n]->get_date(ltime, 0);
00241 temporal.set_years(ltime.year);
00242 temporal.set_months(ltime.month);
00243 temporal.set_days(ltime.day);
00244 temporal.set_hours(ltime.hour);
00245 temporal.set_minutes(ltime.minute);
00246 temporal.set_seconds(ltime.second);
00247 temporal.set_epoch_seconds();
00248 if (temporal.is_valid())
00249 {
00250 time_t tmp;
00251 temporal.to_time_t(tmp);
00252
00253
00254 context->Global()->Set( v8::String::New( args[n]->name ), v8::Date::New(((uint64_t)tmp)*1000) );
00255 } else {
00256 context->Global()->Set( v8::String::New( args[n]->name ), v8::String::New(args[n]->val_str(str)->c_str() ) );
00257 }
00258 } else {
00259 context->Global()->Set( v8::String::New( args[n]->name ), v8::String::New(args[n]->val_str(str)->c_str() ) );
00260 }
00261 }
00262 }
00263 }
00264
00265 context->Global()->Set( v8::String::New("arguments"), a );
00266
00267
00268
00269
00270 v8::TryCatch try_catch;
00271 v8::Handle<v8::Value> result;
00272
00273
00274 v8::Handle<v8::String> source = v8::String::New(source_str->c_str());
00275 v8::Handle<v8::Script> script = v8::Script::Compile(source);
00276 if ( script.IsEmpty() ) {
00277 emit_drizzle_error(&try_catch);
00278 return NULL;
00279 } else {
00280 result = script->Run();
00281 if ( result.IsEmpty() ) {
00282 assert( try_catch.HasCaught() );
00283 emit_drizzle_error( &try_catch );
00284
00285 context->Exit();
00286 context.Dispose();
00287 return NULL;
00288 } else {
00289 assert( !try_catch.HasCaught() );
00290 if ( result->IsUndefined() ) {
00291
00292
00293 context->Exit();
00294 context.Dispose();
00295 return NULL;
00296 }
00297 }
00298 }
00299
00300
00301
00302 v8::Handle<v8::String> rstring = result->ToString();
00303
00304
00305
00306 str->free();
00307 str->alloc( rstring->Utf8Length() );
00308
00309 rstring->WriteUtf8( str->ptr() );
00310
00311 str->length( rstring->Utf8Length() );
00312
00313 context->Exit();
00314 context.Dispose();
00315
00316
00317 null_value= false;
00318 return str;
00319 }
00320
00321
00322
00323
00324 plugin::Create_function<JsFunction> *js_function = NULL;
00325
00326 static int initialize( module::Context &context )
00327 {
00328 js_function = new plugin::Create_function<JsFunction>("js");
00329 context.add( js_function );
00330
00331 v8::V8::Initialize();
00332 return 0;
00333 }
00334
00335
00336
00337
00342 v8::Handle<v8::Value> V8Version( const v8::Arguments& ) {
00343 return v8::String::New( v8::V8::GetVersion() );
00344 }
00345
00350 v8::Handle<v8::Value> JsEngine( const v8::Arguments& ) {
00351 return v8::String::New( JS_ENGINE );
00352 }
00353
00354 }
00355
00356 }
00357
00358 DRIZZLE_DECLARE_PLUGIN
00359 {
00360 DRIZZLE_VERSION_ID,
00361 "js",
00362 "0.9",
00363 "Henrik Ingo",
00364 "Execute JavaScript code with supplied arguments",
00365 PLUGIN_LICENSE_GPL,
00366 drizzle_plugin::js::initialize,
00367 NULL,
00368 NULL
00369 }
00370 DRIZZLE_DECLARE_PLUGIN_END;
00371