Git Product home page Git Product logo

mysqlv8udfs's Introduction

mysqlv8udfs

MySQL UDFs to work with the Google v8 javascript engine

This project provides the code for a MySQL server plugin and a number of MySQL User-defined functions (UDFs) that expose Google's v8 javascript engine in MySQL queries and stored routines. Here's a summary of what it provides:

  • UDF js(script[, arg1, ..., argN]) - execute a snippet of js and return the value.
  • UDF jsudf(script[, arg1, ..., argN]) - exposes MySQL's native UDF interface to the javascript environment.
  • UDF jsagg(script[, arg1, ..., argN]) - exposes MySQL's native UDF interface for aggregate functions to the javascript environment.

In addition, a built-in mysql client API is provided that let's you run MySQL commands and consume results - in Javascript!

How to build

This is the line that works for me:

g++ -Wall -I include -I /home/rbouman/mysql/mysql/include -shared -fPIC -DMYSQL_DYNAMIC_PLUGIN -o mysqlv8udfs.so mysqlv8udfs.cpp /usr/lib/libv8.so ~/mysql/mysql/lib/libmysqlclient.so

(This works on KUbuntu 12.10 LTS, and on Ubuntu 13 Saucy. In my case, mysql is installed (from tar.gz archive) in ~/mysql/mysql. I tried with both MySQL 5.6 and MySQL 5.7. I also installed the g++ and libv8-dev packages using Ubuntu Software Center. v8 version: libv8-3.14, g++ 4.8.1-2)

Here are the exact steps that MAC Users can use for building dylib from your code:

Dependencies:

  1. Brew Installation: ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)”
  2. MySQL Installation : brew install mysql Tested Version: 5.6.14, could work with any mysql 5.6 versions
  3. V8 Engine - 3.15.11 Installation steps:
    1. brew versions v8 - This will list out all versions of v8 that brew has We are interested in version 3.15.11
    2. cd brew —prefix
    3. git checkout cb30f36 Library/Formula/v8.rb
    4. brew install v8
    5. g++ -Wall -I include -I /usr/local/Cellar/mysql/5.6.14/include/mysql -dynamiclib -o mysqlv8udfs.dylib -DMYSQL_DYNAMIC_PLUGIN mysqlv8udfs.cpp /usr/local/Cellar/v8/3.15.11/lib/libv8.dylib /usr/local/Cellar/mysql/5.6.14/lib/libmysqlclient.dylib

(you should modify the paths to match the location of MySQL and libv8 on your system, and the install.sql has to be changed to read from .dylib instead of .so's)

WARNING: It seems OSX comes with a newer version of v8, like 3.21.17 or higher. Unfortunately, this currently won't compile.

Hopefully I can manage to wrap my head around libtool and autoconf and whatnot so I can come up with a build process that works for everybody. If anybody would like to contribute that, then please go ahead, I'd welcome it with open arms :).

How to install

Once you managed to pass the build, you should have a mysqlv8udfs.so file. First, you should place this file in MySQL's plugin dir. You can find out where that is by running this query:

SHOW VARIABLES LIKE 'plugin_dir'

After moving the mysqlv8udfs.so to the plugin dir, you can install the udfs into MySQL by running the install.sql script. You should run the script under MySQL's root account.

If you ran the install.sql script successfully you should now have 3 new UDFs. You can check it by running this query:

mysql> SELECT * FROM mysql.func WHERE name LIKE 'js%';
+-------+-----+----------------+-----------+
| name  | ret | dl             | type      |
+-------+-----+----------------+-----------+
| js    |   0 | mysqlv8udfs.so | function  |
| jsagg |   0 | mysqlv8udfs.so | aggregate |
| jsudf |   0 | mysqlv8udfs.so | function  |
+-------+-----+----------------+-----------+
3 rows in set (0.00 sec)

You should see 3 rows.

Using the js UDF

The js UDF is the simplest but also least powerful. It requires at least 1 argument, which must of the string type and contain a valid javascript snippet.

The UDF will compile the javascript code (if possible, only once) and then run it for each row, returning the value of the last expression in the script (as a string):

mysql> SELECT js('1 + 1');
+-------------+
| js('1 + 1') |
+-------------+
| 2           |
+-------------+
1 row in set (0.00 sec)

You can pass more than one argument to the js function. The values of these extra arguments are exposed to the javascript runtime via the built-in arguments array:

mysql> SELECT js('arguments[0] + arguments[1]', 1, 1);
+-----------------------------------------+
| js('arguments[0] + arguments[1]', 1, 1) |
+-----------------------------------------+
| 2                                       |
+-----------------------------------------+
1 row in set (0.01 sec)

Using the jsudf UDF

The jsudf UDF exposes MySQL's native User-defined function interface to the javascript runtime. (See http://dev.mysql.com/doc/refman/5.6/en/adding-udf.html for more information)

Just like the js function, it expects the first argument to be a string containing a snippet of javascript. Unlike the js function, jsudf expects the script argument to be static (=the same for all rows). It compiles the script once, and runs it immediately.

After running the script, jsudf then looks for a javascript function called udf. This udf function is then called for each row, and its return value is returned to the MySQL query.

mysql> select jsudf('
   -'> function udf(){
   -'>   return 1+1;
   -'> ') jsudf;
+-------+
| jsudf |
+-------+
| 2     |
+-------+
1 row in set (0.00 sec)

Any extra arguments passed to the jsudf function are available in the script. However, instead of only exposing the argument values like the js function does, jsudf exposes argument objects:

mysql> select jsudf('
   -'> function udf(){
   -'>   return JSON.stringify(this.arguments, null, 2);
   -'> }', 'string', 6.626068e-34, 299792458, 3.1415) jsudf;

(The snippet above uses the javascript built-in JSON object to serialize the arguments array to a JSON string, which is returned. Note that inside the udf function, the arguments arrays is referred to using this.arguments rather than arguments.)

The result is something like this:

[
  {
    "name": "'string'",
    "type": 0,
    "max_length": 6,
    "value": "string",
    "maybe_null": false
  },
  {
    "name": "6.626068e-34",
    "type": 1,
    "max_length": 12,
    "value": 6.626068e-34,
    "maybe_null": false
  },
  {
    "name": "299792458",
    "type": 2,
    "max_length": 9,
    "value": 299792458,
    "maybe_null": false
  },
  {
    "name": "3.1415",
    "type": 4,
    "max_length": 6,
    "value": "3.1415",
    "maybe_null": false
  }
]

Each entry in the returned array represents an argument passed to the jsudf function. The fields for these argument objects correspond directly to the UDF_ARGS structure of the MySQL native UDF interface. (See http://dev.mysql.com/doc/refman/5.6/en/udf-arguments.html for more information)

The MySQL UDF interface also supports an init and a deinit function. So does the jsudf: if the initial script contains a init javascript function, then this will be called prior to any calls to the udf function. Likewise, if there is a deinit function, then it will be called after all calls to the udf function have been made.

Using the jsagg UDF

The jsagg function lets you write a MySQL aggregate function in javascript. It's in many ways like the jsudf function: it accepts an initial static string argument which must contain valid javascript. It runs it immediately and then looks for the following javascript functions:

  • init: this will be called prior to any processing.
  • clear: this is called right before processing a group of rows.
  • udf: this is called for each row in the group.
  • agg: this is called right after processing a group of rows. This function should return the aggregated value which is returned to MySQL
  • deinit: this is called after all processing is done.

Here's a simple example that implements COUNT(*) as a javascript function:

mysql> select jsagg('
   -'>   var count;
   -'>   function clear(){
   -'>     count = 0;
   -'>   }
   -'>   function udf(){
   -'>     count++;
   -'>   }
   -'>   function agg(){
   -'>     return count;
   -'>   }
   -'> ') from sakila.category;

mysqlv8udfs's People

Contributors

rpbouman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

mysqlv8udfs's Issues

require not working in mac os

Hi,
I just installed mysqlv8udfs and i tried some simple examples and it works.
But when I try to use the require example, things are not working.
In my Mac, i have a folder ~/mysqlv8udf_samples
And I have the following files and directories:
json_export.sql
js_modules/json_export.js
In json_export.sql, I simply do:
select jsagg('require("json_export.js");', id, name) as json from test.category;
But when I run mysql < json_export.sql, from the mysql error log I got:
Module not found
js_modules/json_export.js
oops, file is NULL

compile on debian 7?

I tried to adapt your command to my system (debian 7 with MariaDB 10 generic binaries):

g++ -Wall -I include -I /usr/local/mysql/include -shared -fPIC -DMYSQL_DYNAMIC_PLUGIN -o mysqlv8udfs.so mysqlv8udfs.cpp /usr/lib/libv8.so /usr/local/mysql/lib/libmysqlclient.so

But it can't find mysql.h, because it's in the include/mysql subdir. So I adjusted the include path, but now it says:

mysqlv8udfs.cpp:7:19: fatal error: mysql.h: File o directory non esistente
compilation terminated.

I can't find a working command for compiling, don't know if it's a bug or just my stupidity.

DECIMAL_RESULT arguments marked as REAL_RESULT

We coerce DECIMAL_RESULT to REAL_RESULT, which is ok. However, the local arguments array for jsagg and jsudf will have those arguments marked as REAL_RESULT. We would like to preserve the information that it was in fact a decimal result originally.

Currently this information is lost because the argument extractors are set up first, and only then do we set up the argument objects.

We need to refactor the way we setup arguments to preserve the info. If possible, and while we're at it we should try and avoid looping over the arguments twice (onece for extractors, and once for argument objects)

Scripting environment should know which UDF is being called.

Would be nice if the scripting environment was aware which udf is being called. We could add a member to the global object, say "context" which would hold the value 'js', 'jsudf', jsagg' etc.

Main purpose is to allow the same script to behave differently according to whether it is an aggregate or a scalar function. This would be esp. useful for reusable scripts, for example a script "json_export.js" could then be written so that it returns an array of docs when used as aggregate, or a single doc per row when used as scalar.

Maybe in addition to (or instead of?) we could have a flag, say, "is_aggregate" to convey whether the udf is an aggregate or not.

mysql.types should get its own proper template

Currently, the mysql.types object is created as a normal object. This means it is mutable.

We should create an object template and write accessors instead. This might save a little bit of resources too, but more importantly it provides a simple path to make the object immutable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.