Archive for the ‘Development’ Category

common_schema, rev. 178: foreach(), repeat_exec(), Roland Bouman, query analysis

Декабрь 1st, 2011

common_schema, revision 178 is now released, with major additions. This revision turns common_schema into a framework, rather than a set of views and functions.

common_schema provides with query scripting, analysis & informational views, and a function library, allowing for easier administration and diagnostics for MySQL. It introduces SQL based tools which simplify otherwise complex shell and client scripts, allowing the DBA to be independent of operating system, installed packages and dependencies.

There's no Perl nor Python, and no dependencies to install. It's just a schema.

Some highlights for the new revision:

  • foreach(), aka $(): loop through a collection, execute callback commands per element.
  • repeat_exec(): a repeat-until device: execute queries until some condition holds.
  • exec_file(): execute files a-la SOURCE, but on server side
  • Query analysis: analyze query text, view or routine definitions to detect dependency objects.
  • Improvements to views and routines, new routines introduced.

Let's take a closer look:

rpbouman

I'm very happy to have Roland Bouman working on this project. He introduced some sophisticated code without which some functionality could not take place. I'm sure I don't need to introduce his great capabilities; I'll just pass the note that it is very good working with him!

foreach()

Introducing a looping device which can iterate a collection and execute callback commands.

What's a collection? A range of numbers; a set of constants; the result set of a SELECT query; tables in your database and more.

What is a callback? A query or set of queries to invoke on the specific elements in the collection. For example:

call foreach('table in sakila', 'ALTER TABLE ${schema}.${table} ENGINE=InnoDB ROW_FORMAT=COMPRESSED');

I'll publish dedicated posts on foreach(), aka $(), following this post. Official documentation is here.

repeat_exec()

Repeat executing queries in a given interval, until some condition holds.

What kind of condition? You can loop forever, or until a given time has passed, a given number of iteration has passed.

You can iterate until no rows are affected by your commands (your callbacks), or until some dynamic condition holds (a query evaluates to true).

For example: purge rows from a table until no more rows are affected; in interval of 3 second:

call repeat_exec(3, 'DELETE FROM test.event WHERE ts < CURDATE() ORDER BY id LIMIT 1000', 0);

Official documentation is here.

exec_file()

If you need to execute commands from a file, you usually invoke SOURCE:

mysql> SOURCE '/tmp/somefile.sql';

Or you invoke mysql client and redirect its input to read from file:

bash$ mysql some_db < /tmp/somefile.sql

SOURCE is a MySQL client command. The file must reside on your client. Running the mysql client is great, but you need to work it out from outside the server.

call_exec() will let you import a file on server side, from within the server:

call exec_file('/tmp/some_file.sql');

You will need to have the file readable; it is limited to 64K at this moment; it may not use DELIMITER, and it may not include dynamic SQL. These are the limitations.

Official documentation is here.

exec() / exec_single()

All of the above rely on the exec() / exec_single() routines, which dynamically execute a set of queries. One one hand, it's no big deal: they only have to use prepared statements in order to invoke the queries. But then, they knows how to parse multiple queries (find the ";" delimiter correctly), plus they allow for configuration: if you set @common_schema_dryrun, queries are not actually executes; just printed out. If you set @common_schema_verbose, queries are verbosed in addition to being executed. Since all execution routines rely on these,we get a standardized pattern.

Official documentation is here.

Query analysis

Query parsing routines allow for detection of dependencies within queries. While not full-blown SQL parser, these allow one to realize on which tables or routines a view depends on; or a routines depends on; or an event; or just any query.

These routines can analyze the text of not only a SELECT query, but also UPDATE, DELETE, CREATE, etc. They can read the code of a stored routines, including queries and control flow constructs; thus, they are also able to analyze events and triggers.

At this stage forward-dependencies resolution is supported. This can eventually lead to dependency graphs or to reverse-dependency resolution (i.e. "which view, routine, trigger or event depend on table t?")

Example:

mysql> call get_view_dependencies('sakila', 'actor_info');
+-------------+---------------+-------------+--------+
| schema_name | object_name   | object_type | action |
+-------------+---------------+-------------+--------+
| sakila      | actor         | table       | select |
| sakila      | category      | table       | select |
| sakila      | film          | table       | select |
| sakila      | film_actor    | table       | select |
| sakila      | film_category | table       | select |
+-------------+---------------+-------------+--------+

The query analysis routines are in BETA stage.

Official documentation is here.

Test quite

common_schema is now tested. Not all code is as yet under tests; all new code is, and some of the older code. Work is in progress to add more and more tests.

Further changes:

  • candidate_keys does not give higher score for PRIMARY KEYs any longer. It ranks all unique keys according to its own heuristic; it also provides with the  is_primary and is_nullable columns.
  • Added candidate_keys_recommended view, recommending best candidate key per table (while noting whether it qualifies as PRIMARY KEY in terms of NULLable columns).
  • Added many text parsing and text manipulation routines, such as better trim, tokenizing, etc. Improved existing code significantly.

Get it

common_schema is available for downloaded. It is released under the BSD license, and is free.

I've put very hard work into common_schema's documentation. It is very thorough and provides with clear examples. The documentation is also available for download.

If you encounter problems, please report on the issues page.

common_schema is meant to be downloaded & installed on any MySQL server. It provides with general and essential functionality. Spread the word!


PlanetMySQL Voting: Vote UP / Vote DOWN

Writing a MariaDB PAM Authentication Plugin

Ноябрь 17th, 2011

As you know, since version 5.2.0 (released in April 2010) we support Pluggable Authentication. Using this feature one can implement an arbitrary user authentication and account managing policy, completely replacing built-in MariaDB authentication with its username/password combination and mysql.user table.

Also, as you might have heard, Oracle has recently released a PAM authentication plugin for MySQL. Alas, this plugin will not run on MariaDB — although MySQL implementation of pluggable authentication is based on ours, the API is incompatible. And, being closed source, this plugin cannot be fixed to run in MariaDB. And — I’m not making it up — this plugin does not support communication between the client and the server, so even with this plugin and all the power of PAM the only possible authentication method remains username/password combination.

But writing authentication plugins is easy, I said to myself. I will do my own authentication plugin! With blackjack and hookers.

I start from installing the development headers:

sudo rpm -ivh MariaDB-devel-5.2.9-102.el5.x86_64.rpm

On Debian or Ubuntu you would’ve need to install libmariadbclient-dev. By the way, a disclaimer — I’m doing it for MariaDB-5.2, but with minimal changes this plugin can work with MySQL-5.5 too.

Now I create a working directory and, being a lazy guy, copy auth_socket plugin  sources — one of authentication plugins that come with MariaDB — from Launchpad (only auth_socket.c). Stripped down, with the old code removed, it becomes my pam.c:

#define MYSQL_DYNAMIC_PLUGIN
#include <mysql/plugin_auth.h>

static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{
}

static struct st_mysql_auth pam_info =
{
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
  "dialog",
  pam_auth
};

mysql_declare_plugin(pam)
{
  MYSQL_AUTHENTICATION_PLUGIN,
  &pam_info,
  "pam",
  "Sergei Golubchik",
  "PAM based authentication",
  PLUGIN_LICENSE_GPL,
  NULL,
  NULL,
  0x0100,
  NULL,
  NULL,
  NULL
}
mysql_declare_plugin_end;

At the end of the file we have the plugin descriptor — it always has the same structure for all plugin types. Above it — authentication plugin descriptor, it tells MariaDB what function performs the actual authentication, and what plugin should the client use.

Let me repeat — what plugin should the client use. Indeed, an authentication process is always a dialog. The server asks questions (“username?”, “password?”), the client answers them. Because a loadable plugin may cause the server to ask the most unexpected question (“the fingerprint of the left index finger?”), the client should support plugins too — that know how to answer them. And it does support them — or, more precisely, libmysqlclient does, automatically and transparently for the client applications.

In this particular case, though, the questions aren’t very exotic. PAM may only ask the end user to enter some text, so the client plugin needs to be able to print a prompt text, read the user’s input, and send it back to the server. And repeat until the server is satisfied. Luckily, MariaDB already has a plugin to perform such a dialog with the user. The plugin is called, not surprisingly, dialog, and in the my plugin descriptor, I specify that pam server plugin needs client to load dialog plugin to be able to continue the authentication.

Now, let’s see if this plugin skeleton works:

$ gcc -o pam.so pam.c `mysql_config --cflags` -shared -fPIC -lpam

It compiles and even loads into the server. Looks good so far, and I open man pam.

According to the man pages, to perform a PAM authentication one needs to do the following:

  1. initialize the PAM subsystem with the pam_start() function.
  2. invoke pam_authenticate() that performs the actual authentication
  3. verify user’s account with the pam_acct_mgmt()
  4. in the process of authentication, PAM can change the user name. Retrieve the new name with pam_get_item(PAM_USER)
  5. at the end one should always call pam_end()

To talk to the client, PAM allows to specify a conversation function — my function that PAM will invoke as necessary.

I put the above logic into the main pam_auth() function:

#include <string.h>
#include <security/pam_modules.h>
#include <security/pam_appl.h>

static int conv(int n, const struct pam_message **msg,
                struct pam_response **resp, void *data)
{
}

#define DO(X) if ((status = (X)) != PAM_SUCCESS) goto end

static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{
  pam_handle_t *pamh = NULL;
  int status;
  const char *new_username;
  struct param param;
  struct pam_conv c = { &conv, &param };

  /* get the service name, as specified in

     CREATE USER ... IDENTIFIED WITH pam_auth AS  "service"

  */
  const char *service = info->auth_string ? info->auth_string : "mysql";

  param.ptr = param.buf + 1;
  param.vio = vio;

  DO( pam_start(service, info->user_name, &c, &pamh) );
  DO( pam_authenticate (pamh, 0) );
  DO( pam_acct_mgmt(pamh, 0) );
  DO( pam_get_item(pamh, PAM_USER, (const void**)&new_username) );

  if (new_username)
    strncpy(info->authenticated_as, new_username,
            sizeof(info->authenticated_as));

end:
  pam_end(pamh, status);
  return status == PAM_SUCCESS ? CR_OK : CR_ERROR;
}

The plugin is almost done. The only missing bit is the conversation function conv(). According to the PAM documentation, it will be invoked with an array of “questions”, that should be shown to the user, and it must return his answers. Additionally, it will get one opaque pointer argument — callback functions almost always have it in almost all APIs in the world. From this function I will send the “questions” to the client, and receive the answers. The dialog plugin on the client side will do the actual communication with the user.

Sending and receiving is easy in Pluggable Authentication API. One of the arguments of the main authentication function — pam_auth() in our case — is a so called vio handle. This handle provides read_packet() and write_packet() functions, that the client and server plugins can use to communicate with each other. The server will take care of everything else — delivering packets, splitting and reassembling them, encrypting (if SSL is used), using unix sockets, tcp/ip, named pipes, shared memory, making sure that the server plugin talks to a right client plugin, maintaining backward compatible protocol on the wire, and so on. That’s, by the way, where the name vio comes from — it means Virtual I/O.

There is one last difficulty to overcome. PAM can four different types of messages, two of them being purely informational, with the meaning “print this to user”, and two being input messages, with the meaning “print this and read the reply”. While dialog plugin supports only “print this and read the reply” kind of actions. To solve this API mismatch, our conversation function will accumulate PAM informational messages until it sees the first input message. Then it’ll send all accumulated and concatenated messages to the dialog plugin as one big prompt string, in one packet. This is what I mean:

struct param {
  unsigned char buf[10240], *ptr;
  MYSQL_PLUGIN_VIO *vio;
};

static int conv(int n, const struct pam_message **msg,
                struct pam_response **resp, void *data)
{
  struct param *param = (struct param *)data;
  unsigned char *end = param->buf + sizeof(param->buf) - 1;
  int i;

  for (i = 0; i < n; i++) {
    /* if there's a message - append it to the buffer */
    if (msg[i]->msg) {
      int len = strlen(msg[i]->msg);
      if (len > end - param->ptr)
        len = end - param->ptr;
      memcpy(param->ptr, msg[i]->msg, len);
      param->ptr+= len;
      *(param->ptr)++ = '\n';
    }
    /* if the message style is *_PROMPT_*, meaning PAM asks a question,
       send the accumulated text to the client, read the reply */
    if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
        msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
      int pkt_len;
      unsigned char *pkt;

      /* allocate the response array.
         freeing it is the responsibility of the caller */
      if (*resp == 0) {
        *resp = calloc(sizeof(struct pam_response), n);
        if (*resp == 0)
          return PAM_BUF_ERR;
      }

      /* dialog plugin interprets the first byte of the packet
         as the magic number.
           2 means "read the input with the echo enabled"
           4 means "password-like input, echo disabled"
         C'est la vie. */
      param->buf[0] = msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? 2 : 4;
      if (param->vio->write_packet(param->vio, param->buf, param->ptr - param->buf - 1))
        return PAM_CONV_ERR;

      pkt_len = param->vio->read_packet(param->vio, &pkt);
      if (pkt_len < 0)
        return PAM_CONV_ERR;
      /* allocate and copy the reply to the response array */
      (*resp)[i].resp = strndup((char*)pkt, pkt_len);
      param->ptr = param->buf + 1;
    }
  }
  return PAM_SUCCESS;
}

That’s all. Now I can compile it as above (repeating the process twice, because I forgot -lpam the first time), load it, configure PAM to use pam_skey for the “mysql” service, create the user and, finally, login:

$ mysql -u root
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 1
Server version: 5.2.9-MariaDB-debug Source distribution

MariaDB []> CREATE USER serg IDENTIFIED VIA pam USING 'mysql';
Query OK, 0 rows affected (0.00 sec)

MariaDB []> ^DBye
$ mysql -u serg
challenge otp-md5 99 th91334
password: <Enter>
(turning echo on)
pasword: OMEN US HORN OMIT BACK AHOY
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 5.2.9-MariaDB-debug Source distribution

MariaDB []> SELECT "Hey-ho! It works!!!


PlanetMySQL Voting: Vote UP / Vote DOWN

Bzr and launchpad tricks: firefox plugin

Ноябрь 7th, 2011

If you work with bazaar, you have seen its URIs. You can find the complete list is in the bzr help urlspec. Although I commonly use only a subset of that, like bzr+ssh://bazaar.launchpad.net/~maria-captains/maria/5.2-serg/ and http://bazaar.launchpad.net/%2Bbranch/mysql-server/5.5/.

In addition I often use Launchpad aliases, such as lp:~maria-captains/maria/5.3-serg/, lp:maria/5.3, and lp:869001.

And finally, there are common abbreviations that we have used in MySQL, and others that we use in MariaDB, for example bug#12345 and wl#90.

What’s annoying, I need to remember that wl#90 corresponds to http://askmonty.org/worklog/?tid=90 and type the latter in the location bar of the browser, when I want to look this task up. And lp:869001 is, for my browser, https://bugs.launchpad.net/bugs/869001. Similarly, every other URL above, has its browser-friendly evil twin. It’s evil, because I have to remember it!

Now, Firefox tries to help, to a certain extent. It supports so-called keywords — short aliases for bookmarks. Create a bookmark for https://bugs.launchpad.net/bugs/%s and in the Keyword field enter lp. Now you can type in the location bar lp 869001 (with a space) and Firefox will expand it into a complete url https://bugs.launchpad.net/bugs/869001. Quite handy. And I’ve used it for a few years. Still it annoyed me, that I had to rewrite the abbreviations manually, put spaces, remove colons, and so on. And at last it annoyed me to a degree where I wrote a Firefox plugin!

Let me introduce a LocationMorph — a plugin that can arbitrarily rewrite the text in the location bar, according to the user specified regular expression. I have configured it (via a Preference dialog) to use the following set of rules

^bzr\+ssh://bazaar.launchpad.net/ http://code.launchpad.net/
^lp:(\d+)$ https://bugs.launchpad.net/bugs/$1
^lp: http://code.launchpad.net/
^wl#(\d+)$ http://askmonty.org/worklog/?tid=$1
^bug#(\d+)$ http://bugs.mysql.com/bug.php?id=$1

And now I can simply copy and paste a bzr URI, or a launchpad alias, or our internal abbreviation from the email (or bzr info) directly into the browser, and it understands it directly and shows me the page that I want. Ahh, perfection…


PlanetMySQL Voting: Vote UP / Vote DOWN

Test-driven SQL development

Октябрь 20th, 2011

I'm having a lot of fun writing common_schema, an SQL project which includes views, tables and stored routines.

As the project grows (and it's taking some interesting directions, in my opinion) more dependencies are being introduced, and a change to one routine or view may affect many others. This is why I've turned the development on common_schema to be test driven.

Now, just how do you test drive an SQL project?

Well, much like the way you test any other project in your favorite programming language. If its functions you're testing, that's all too familiar: functions get some input and provide some output. Hmmm, they might be changing SQL data during that time. With procedures it's slightly more complex, since they do not directly return output but result sets.

Here's the testing scheme I use:

  • Tests are divided to families. For example, there is a family of tests for the eval() function.
  • Each test in a family is responsible for checking the simplest, most "atomic" issue. This means many small tests.
  • Each test can have a "pre-test" step, which prepares the ground (for example, create a table and populate it)
  • Likewise, a test can have a "post-test" step, which is typically just cleanup code (since the test is already complete by the time the post step is invoked).
  • Each test is an SQL file: a set of commands to be executed.
  • A test may have an "expected output" file.
  • If no explicit expected exists, the test is expected to return "1" (just as the most basic JUnit test assumes an "assert true")
  • A test family may also have pre- and post- steps.
  • Any failure in any step fails the entire process. Failures may include:
    • Failure to prepare the grounds for a test or family of tests
    • Failure in executing the test
    • Mismatch between test's output and expected result.
    • Failure in executing the post- step (may indicate yet invalid test result not intercepted by the test)

An example

The following image presents a single test family: the eval family, testing the eval() routine.

Test driven SQL development - sample

  • In this family, there are two tests.
  • In both tests, we have a pre-test step, and a test.
  • We have no post-test here.
  • Nor do we have an expected-output sample, which means the tests expect to return with "1".

Implementation

But how are tests conducted? Via mysql, of course. While tests are plain SQL text file, they are being executed against a running MySQL server using the mysql client. It is given the test files as input, and its output is directed to file as well.

This makes it very easy to code the test using a simple shell script. It takes a small measure of file iteration, process invocation, exit code check, and diff execution.

For example, to test eval()'s 01 test, we first execute mysql with 01/pre.sql as input. Assuming success, we execute mysql again, this time with 01/test.sql. We capture the output of this execution, and compare it with expected-output, or with "1" when no expected-output specified.

Tests pass, or no code!

Some 12 years ago, I worked with a less-known version system called aegis. The thing I remember most from aegis was that it had a good tests infrastructure. Long before "test-driven development" was coined, or was even commonly practiced, aegis supported tests right into your version control. "Right into", in the sense that you could not merge your code back to the baseline if it didn't pass all of the tests.

I work with SVN for common_schema, and I do not know of such an option in SVN. But I also use ant to build this project, and the dependency is clear there: ant dist, my target which creates the distribution files, is dependent on ant test, the target which works out the tests.

That is, you cannot generate the distribution files when tests fail.

Further notes

Since I'm now retroactively patching tests for already existing functionality, calling it test-driven development is an overstatement; nevertheless new tests are already proving invaluable when I keep changing and improving existing code. Suddenly dependent functionality no longer works as expected. What fun!

The code for the testing suite is actually much shorter than this blog post.


PlanetMySQL Voting: Vote UP / Vote DOWN

TaskFreak! v0.6.2 – Customizing Status

Октябрь 3rd, 2011

Background Knowledge


The progress of a task in TaskFreak! is shown as a percentage value and is not exactly visually appealing to quickly spot the progress. With a few minor alterations we can show the percentage completed bar that fills as the task progresses and a gradient bar indicating the progress along with the percentage value.

This solution was posted by Searcher at Re: Taskfreak Customizing Status.

Solution


  1. Edit at line #268 as shown below.
    Cod Before
    268
    
    <th width="<?php echo FRK_STATUS_LEVELS * 2; ?>%" onclick="freak_sort('statusKey')" colspan="< ?php echo FRK_STATUS_LEVELS ?>" class="sortable">< ?php echo (FRK_STATUS_LEVELS == 1)?'X':$langForm['status']; ?></th>
    Code After
    268
    
    <th width="80" onclick="freak_sort('statusKey')" class="sortable">< ?php echo (FRK_STATUS_LEVELS == 1)?'X':$langForm['status']; ?></th>
  2. Edit at line #382
    Code Before
    < ?php
                        $s = $objItem->itemStatus->statusKey;
                        for ($i = 0; $i < FRK_STATUS_LEVELS; $i++) {
                            $j = ($i < $s)?(FRK_STATUS_LEVELS - $i):0;
                    ?>
                            <td id="est<?php echo ($i+1).$objItem->id; ?>" class="sts< ?php echo $j; ?>"< ?php
                            if ($objUser->checkLevel(14) || $objItem->checkRights($objUser->id,8,true))  {
                                echo ' onclick="freak_sts('.$objItem->id.','.($i+1).')" style="cursor:pointer"';
                            }
                        ?>>&nbsp;</td>
                    < ?php
                        }
                    ?>
    Code After
    <!-- status bar update -->
              <td>
                <table width="100%" cellpadding="0" cellspacing="0">
                  <tr>
                    < ?php
                    $s = $objItem->itemStatus->statusKey;
                    for ($i = 0; $i < FRK_STATUS_LEVELS; $i++) {
                      $j = ($i < $s)?(FRK_STATUS_LEVELS - $i):0;
                      ?>
                      <td width="10" onmouseover="this.style.cursor='pointer'" id="est<?php echo ($i+1).$objItem->id; ?>" class="sts< ?php echo $j; ?>"< ?php if ($objUser->checkLevel(14) || $objItem->checkRights($objUser->id,8,true))  { echo ' onclick="freak_sts('.$objItem->id.','.($i+1).')" style="cursor:pointer"'; } ?>>&nbsp;</td>
                      < ?php } ?>
                      <td style="border:0"></td>
                      <td id="status_perc_<?php echo $objItem->id; ?>" style="border:0; font-size:8px; color:#000" align="right">&nbsp;< ?php echo ($s*20)."%"; ?></td>
                    </tr>
                    <tr><td style="border:0; height:2px"></td></tr>
                    <tr>
                      <td colspan="7" style="border:0"><img id="status_bar_<?php echo $objItem-/>id; ?>" src="skins/status.jpg" height="5" width="< ?php echo ($s*16); ?>" /></td>
                  </tr>
                </table>
              </td>
    <!-- status bar update -->
  3. Download the status gradient bar image file from “http://demofreak.dracon.biz/skins/status.jpg” and the copy image file to the /taskfreak/skins/ directory.

Source: Taskfreak Customizing Status


PlanetMySQL Voting: Vote UP / Vote DOWN

TaskFreak! v0.6.2 – Customizing Status

Октябрь 3rd, 2011

Background Knowledge


The progress of a task in TaskFreak! is shown as a percentage value and is not exactly visually appealing to quickly spot the progress. With a few minor alterations we can show the percentage completed bar that fills as the task progresses and a gradient bar indicating the progress along with the percentage value.

This solution was posted by Searcher at Re: Taskfreak Customizing Status.

Solution


  1. Edit at line #268 as shown below.
    Cod Before
    268
    
    <th width="<?php echo FRK_STATUS_LEVELS * 2; ?>%" onclick="freak_sort('statusKey')" colspan="< ?php echo FRK_STATUS_LEVELS ?>" class="sortable">< ?php echo (FRK_STATUS_LEVELS == 1)?'X':$langForm['status']; ?></th>
    Code After
    268
    
    <th width="80" onclick="freak_sort('statusKey')" class="sortable">< ?php echo (FRK_STATUS_LEVELS == 1)?'X':$langForm['status']; ?></th>
  2. Edit at line #382
    Code Before
    < ?php
                        $s = $objItem->itemStatus->statusKey;
                        for ($i = 0; $i < FRK_STATUS_LEVELS; $i++) {
                            $j = ($i < $s)?(FRK_STATUS_LEVELS - $i):0;
                    ?>
                            <td id="est<?php echo ($i+1).$objItem->id; ?>" class="sts< ?php echo $j; ?>"< ?php
                            if ($objUser->checkLevel(14) || $objItem->checkRights($objUser->id,8,true))  {
                                echo ' onclick="freak_sts('.$objItem->id.','.($i+1).')" style="cursor:pointer"';
                            }
                        ?>>&nbsp;</td>
                    < ?php
                        }
                    ?>
    Code After
    <!-- status bar update -->
              <td>
                <table width="100%" cellpadding="0" cellspacing="0">
                  <tr>
                    < ?php
                    $s = $objItem->itemStatus->statusKey;
                    for ($i = 0; $i < FRK_STATUS_LEVELS; $i++) {
                      $j = ($i < $s)?(FRK_STATUS_LEVELS - $i):0;
                      ?>
                      <td width="10" onmouseover="this.style.cursor='pointer'" id="est<?php echo ($i+1).$objItem->id; ?>" class="sts< ?php echo $j; ?>"< ?php if ($objUser->checkLevel(14) || $objItem->checkRights($objUser->id,8,true))  { echo ' onclick="freak_sts('.$objItem->id.','.($i+1).')" style="cursor:pointer"'; } ?>>&nbsp;</td>
                      < ?php } ?>
                      <td style="border:0"></td>
                      <td id="status_perc_<?php echo $objItem->id; ?>" style="border:0; font-size:8px; color:#000" align="right">&nbsp;< ?php echo ($s*20)."%"; ?></td>
                    </tr>
                    <tr><td style="border:0; height:2px"></td></tr>
                    <tr>
                      <td colspan="7" style="border:0"><img id="status_bar_<?php echo $objItem-/>id; ?>" src="skins/status.jpg" height="5" width="< ?php echo ($s*16); ?>" /></td>
                  </tr>
                </table>
              </td>
    <!-- status bar update -->
  3. Download the status gradient bar image file from “http://demofreak.dracon.biz/skins/status.jpg” and the copy image file to the /taskfreak/skins/ directory.

Source: Taskfreak Customizing Status


PlanetMySQL Voting: Vote UP / Vote DOWN

Welcome, MySQL commercial extensions

Сентябрь 16th, 2011
I saw yesterday that MySQL has finally done the right thing, and announced new commercial extensions.
What this means is that paying customers receive something more than users who get the community edition for free.
Believe it or not, when I was working in the community team at MySQL, I was already an advocate of this solution. You may see a contradiction, but there isn't. I would like to explain how this works.

An open source product needs to be developed. And the developers need to get paid. Ergo, the company needs to make money from that product if it wants to continue developing it. Either that, or the company needs to sell something else to pay the bills. (Let's not get into the argument that a pure open source project with universal participation is better, faster, or more marvelous: MySQL was never that, not with Oracle, not with Sun, and not when it was an independent company. If you want a extra-super-ultra open project, go with Drizzle. With MySQL, we need to continue reasoning with the raw material at hand.)
When MySQL was scaling its business, it realized that many customers were not willing to pay for support and consulting alone. To expand the business beyond the simple offer of services, the company created MySQL Network, which soon evolved into MySQL Enterprise, with the addition of the MySQL Monitoring tools and a fast upgrade plan. This was a good proposal for small to medium customers, but not as good for customers with large installations. When you deploy thousands of MySQL servers, you really don't want to upgrade every month. Anyway, for some time, the value proposition from MySQL was that the community users would get one release twice a year, and the paying customers would get one every month.
As a community manager, I strongly objected to that formula, not only because it hurts the community, but also because it hurts customers. When the release is exposed to millions of free users before it goes to the paying customers, chances are that serious bugs are discovered by the community and fixed in due time, before it hurts a high profile customer and needs to be fixed in a hurry at higher cost. One of the main values of MySQL is that it's that its large community adoption and feedback increases stability. Fortunately, I was not the only one who believed that larger distribution is valuable for customers, and the decision was reversed at the end of 2008.
In that period, I and other employees recommended a different value proposition for our customers. Instead of selling fast upgrade plans (which become a liability), MySQL could develop some reserved features that would be given only to paying customers.
There are two problems with reserved features, though: you need to develop them internally. You can't start them in the open, asking the community to test them for bugs, and then give them only to customers when they are ready (There was a faux pas in that direction in early 2008, but it was promptly retracted). These features must be developed as closed source, and tested only internally. The second problem is that MySQL had little internal QA manpower when this discussion arose.
There was another issue, namely that the code base for the next intended version (the fabled 6.0) was brittle. After 2 years in alpha stage, there was little to show for the effort. In parallel to the Oracle acquisition, two important things happened: version 6 was abandoned, and a new effort was started, using the more stable version 5.x as a code base, and a new developing model was launched, based on milestones and robustness.
This new foundation, combined with the injection of experienced QA personnel from the ranks of Sun and Oracle, made the project ready to offer reserved features to customers, while continuing the development of a lot more features for the community edition.
From a community standpoint, I welcome the commercial extensions. It means that the MySQL project will get more revenues, and be able to sustain the public release more easily. In my opinion it's the logical evolution of the project, and it's what MySQL should have done already years ago if it had had the manpower.
There are already detractors who see this release as a sign of the apocalypse. They probably want to see only this one feature in the commercial arena, dismissing the dozens of new features released to the general public under Oracle stewardship. I refuse to enroll in the chorus of critics who judge Oracle on prejudice. I am so far satisfied with what Oracle has done with MySQL. My opinion is based on facts, and since the facts are in favor of the community, I am pleased to say so. If future facts will make me change my opinion, I will say it. But now, I want to say thank you to the MySQL team at Oracle!

PlanetMySQL Voting: Vote UP / Vote DOWN

DbCharmer 1.7.0 Release: Rails 3.0 Support and Forced Slave Reads

Сентябрь 1st, 2011

This week, after 3 months in the works, we’ve finally released version 1.7.0 of DbCharmer ruby gem – Rails plugin that significantly extends ActiveRecord’s ability to work with multiple databases and/or database servers by adding features like multiple databases support, master/slave topologies support, sharding, etc.

New features in this release:

  • Rails 3.0 support. We’ve worked really hard to bring all the features we supported in Rails 2.X to the new version of Rails and now I’m proud that we’ve implemented them all and the implementation looks much cleaner and more universal (all kinds of relations in rails 3 work in exactly the same way and we do not need to implement connection switching for all kinds of weird corner-cases in ActiveRecord).
  • Forced Slave Reads functionality. Now we could have models with slaves that are not used by default, but could be turned on globally (per-controller, per-action or in a block). This is a new feature that brings our master/slave routing capabilities to a really new level – we could now use it for a really mission-critical models on demand and not be afraid of breaking major functionality of our applications by switching them to slave reads.
  • Lots of changes were made in the structure of our code and tests to make sure it would be much easier for new developers to understand DbCharmer internals and make changes in its code.

Along with the new release we’ve got a brand new web site. You can find much better, cleaner and, most importantly, correct documentation for the library on the web site. We’ll be adding more examples, will try to add more in-depth explanation of our core functions, etc.

If you have any questions about the release, feel free to ask them in our new mailing list: DbCharmer Users Group.

For more updates in our releases, you can follow @DbCharmer on Twitter.



PlanetMySQL Voting: Vote UP / Vote DOWN

CodeBits — An event of competitive innovation

Август 15th, 2011
Codebits 2009 - Pedro and RupertIt was my pleasure and privilege to attend Codebits in 2009. As Roland Bouman says, its talk choice method is based on public voting, and therefore everyone cha have contribute to the schedule.But that is not the main reason for attending this extraordinary event. It is not just a conference. It's an innovation fest. For 1 and 1/2 days, it's a conference, where the speakers are encouraged to bring to their audience the most innovative and inspiring talks. In the afternoon of the second day, the event becomes a competition, where the teams that have registered will have 24 hours to bring a project to completion, and they have to start and finish within the allotted time. The project can be anything, and I have seen quite a lot of exciting stuff rolling live in the huge pavilion: I could hardly ignore robotics, as these little mechanical smurfs were running all over the place and you would have to be careful not to squash them when you walked.There was plenty of occasions for planning of great projects, together with attempts at improving social relations, and mixing up with big brother.There were projects based on 3D printing, and less broad projects like all-seasons keyboards.A very popular session, followed by practical workshops was lock picking. I attended one of them, learned how to pick simple and less simple locks, and I brought home some lockpicking tools.On a more technical level, I was there with Lenz Grimmer and Kai Seidler, we spoke about MySQL and other cool things, and we had lots of fun for three days.Besides the teams hacking away at their projects, there were several teams showcasing technology that had been developed by winners of the previous years, such as 3D television and intelligent phone networks. In short, This was an inspiring event, which I can warmly recommend.

PlanetMySQL Voting: Vote UP / Vote DOWN

Speaking at “August Penguin 2011″

Август 3rd, 2011

I will be speaking at August Penguin 2011 (אוגוסט פינגווין), on August 12th in Ramat-Gan, Israel.

August Penguin is the annual meeting of Hamakor society: an Israeli society for Free Software and Open-Source Code (read more here).

I’ll be holding a non-technical talk about MySQL, titled “MySQL and the Open Source Sphere”. In this talk I will be presenting my impressions of the nature of open source development of MySQL and surroundings: the core server, the various forks, patches, 3rd party tools, companies involved, etc. So this is a general “get to know who’s who & what’s what in the MySQL world”.

This is a 30 minutes talk. I will surely not cover every open source development in this field, so my apologies in advance to those I leave out. The truth is, there’s so much going on lately I can hardly keep up with reading the announcements. Truly, this is a wonderful time for open source development with MySQL.

Talk will be held in Hebrew.

See you there!


PlanetMySQL Voting: Vote UP / Vote DOWN