Thứ Ba, 26 tháng 10, 2010

Tự viết function cho mysql sử dụng Ansi C

Khi các hàm được cung cấp sắn của mysql là không đủ dùng .

Chúng ta nghĩ đến việc viết function hoặc procedure .

Nếu việc viết procedure hướng đến việc sử lý dữ liệu nội tại của database .

Thì việc viết function lại hướng đến các tiện ích làm việc trên các kiểu dữ liệu cơ bản của mysql .

Trong bài viết này mình sẽ hướng dẫn viết function ( để extends mysql ) .

1.Chuẩn bị thư viện.

sudo apt-get install libmysql++-dev

2.Dùng một Editor để soạn file C

include header :

#include <mysql/mysql.h>

Viết hàm init

int hamming_dist_init(UDF_INIT *initid, UDF_ARGS *args,
char *message)

Trong ví dụ này mình viết hàm hamming_dist nên hàm init cho nó sẽ là hamming_dist_init .

Hàm này sẽ được gọi ngay khi hàm chính được gọi . Thường việc tiền sử lý sẽ được viết ở đây ( ví dụ như việc check param).

int hamming_dist_init(UDF_INIT *initid, UDF_ARGS *args,
char *message)
{
if (args->arg_count != 2)
{
strcpy(message,"hamming_dist() requires two arguments");
return 1;
}
return 0;
}

Kiểm tra xem số tham số có =2 hay không . Nếu khác 2 thì thôgn báo lỗi và kết thúc hàm .

Viết hàm chính

Hàm chính có mẫu như sau :

long long hamming_dist(UDF_INIT *initid, UDF_ARGS *args,char *is_null, char *error)

Đây là mẫ hàm trả về kiểu long64 nếu định trả về String , mẫu sẽ khác . Hay muốn viết các hàm Aggregate sẽ dùng mẫu này :

void xxx_reset(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);


Đầy đủ hàm mình đang viết :
long long hamming_dist(UDF_INIT *initid, UDF_ARGS *args,char *is_null, char *error)
{


uint64_t a1 = *((uint64_t*) args->args[0]);
uint64_t a2 = *((uint64_t*) args->args[1]);


uint32_t v1 = a1^a2;
uint32_t v2 = (a1^a2)>>32;

v1 = v1 - ((v1>>1) & 0x55555555);
v2 = v2 - ((v2>>1) & 0x55555555);
v1 = (v1 & 0x33333333) + ((v1>>2) & 0x33333333);
v2 = (v2 & 0x33333333) + ((v2>>2) & 0x33333333);
int c1 = (((v1 + (v1>>4)) & 0xF0F0F0F) * 0x1010101) >> 24;
int c2 = (((v2 + (v2>>4)) & 0xF0F0F0F) * 0x1010101) >> 24;

return c1+c2;
}

Mình thực hiện một số tính toán rồi trả về kiểu long long (64 bit)

Các bạn có thể viết hàm đơn giản như hàm add
long long add(UDF_INIT *initid, UDF_ARGS *args,char *is_null, char *error)

{

long a = *((long*) args->args[0]);

long b = *((long*) args->args[0]);

return a+b;

}

Nhìn qua 2 hàm trên các bạn có thể hiểu cách lấy tham số .(args <-- nó đấy ).

3.Compile :

gcc -shared -o hamming.so -std=c99 $(mysql_config --cflags) hamming_dist.c  $(mysql_config --libs)

Nhớ sửa tên source file và output file nhé  ( xem lại bài trước về các compile http://i-php.net/2010/10/ket-noi-den-csdl-mysql-su-dung/ )

4.Triển khai .

copy file hamming.so vừa tạo ra được vào thu mục plugin của mysql .
sudo cp hamming.so  /usr/lib/mysql/plugin/

Trên các OS khác thì thư mục này sẽ khác nhau , và cái này có thể config được trong file my.conf ( hoặc mysql.ini )

Thực hiện lệnh tạo function trong mysql
CREATE FUNCTION hamming_dist  RETURNS INT SONAME 'hamming.so';

5.Kiểm tra select hamming_dist(1121234123123,131231313131234);

Chúc các bạn thành công .

Thứ Sáu, 22 tháng 10, 2010

Kết nối đến csdl Mysql sử dụng Ansi C

Kết nối đến csdl Mysql sử dụng Ansi C


Bài viết của mình được thực hiện trên Ubuntu nhé ( và Centos ) các bác sài win đi chỗ khác xem nhé  .

1. Cài đặt thư viện

sudo apt-get install libmysql++-dev

2. Code :

Dùng một Editor để soạn file C .

include header :

#include <mysql/mysql.h>

Kết nối :
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;

char *server ="localhost";
char *user = "root";
char *password = "1";
char *database = "mysql";

MYSQL_STMT    *stmt;
MYSQL_BIND    bind[2];

conn = mysql_init(NULL);
if(!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0))
{
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}

Truy vấn :
if (mysql_query(conn, sql)) {
fprintf(stderr, "%s\n", mysql_error(conn));
exit(0);
}

res = mysql_use_result(conn);

while ((row = mysql_fetch_row(res)) != NULL)
{

printf("data  %d \n", row[0]));

}

Nhìn chung C API khá giống php API nên cũng không khó nắm bắt lắm .

Vấn đề chủ yêu nằm ở các kiểu dữ liệu giữa C và Mysql :

Để giải quyết vấn đề này nên sử dụng triệt để prepare statement :

ví dụ :
#define UPDATE_SQL "UPDATE articles category_id=? where article_id =? "

if (mysql_stmt_prepare(stmt, UPDATE_SQL, strlen(UPDATE_SQL)))
{
fprintf(stderr, " mysql_stmt_prepare(), UPDATE failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
exit(0);
}
fprintf(stdout, " prepare, INSERT successful\n");

printf("a");

memset(bind, 0, sizeof(bind));

/* INTEGER PARAM */
/* This is a number type, so there is no need
to specify buffer_length */
bind[0].buffer_type= MYSQL_TYPE_LONGLONG;
bind[0].buffer= (char *)&hash1;
bind[0].is_null= 0;
bind[0].length= 0;
bind[0].is_unsigned=1;

bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= (char *)&article_id;
bind[1].is_null= 0;
bind[1].length= 0;
bind[1].is_unsigned=1;

/* Bind the buffers */
if (mysql_stmt_bind_param(stmt, bind))
{
fprintf(stderr, " mysql_stmt_bind_param() failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
exit(0);
}

/* Execute the INSERT statement - 1*/
if (mysql_stmt_execute(stmt))
{
fprintf(stderr, " mysql_stmt_execute(), 1 failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
exit(0);
}

Nhìn chung code vất vả hơn php gấp 10 lần :( .

Nhưng tốc độ thì khỏi nói .

Dự án của mình sau này chắc  có đển vài trăm ngàn bảng ghi , mà nó chạy như hack ấy :)) .

3.Complile :

gcc -o my.out -std=c99 $(mysql_config --cflags) mycode.c $(mysql_config --libs)

Có 2 tham số hơi lạ mysql_config --cflags <-- để chỉ ra đường dẫn tới mysql header

mysql_config --libs   <-- Để chỉ ra file lib của mysql ( sử dụng khi linking )

Các bạn có thể chạy lệnh này độc lập xem nó sinh ra cái gì .

Happy coding .

Bài viết tiếp theo sẽ là : Tự viến hàm mở rộng cho mysql ( tất nhiên là dùng C )

Thứ Năm, 21 tháng 10, 2010

Tự build extension cho php

Tự build extension cho php


Mình viết tut này trên ubuntu , các distro khác có thể sẽ khác .

1.check out php5 source :

Mình sec check out từ svn vào thư mục /var/lib/php5 nhé .

sudo svn checkout  http://svn.php.net/viewvc/php/php-src/trunk

Nếu ngại sudo nhiều thì có thể dùng sudo su để thành root luôn cho tiện .

2. Cài php5-dev

sudo apt-get install php5-dev

3. Trong thư mục checkout về , trong thư mục ext sẽ có một file ext_skel.sh file này để sinh ra template cho một ext .

Trong ví dụ nay mình đang xây dựng thư viện simhash (http://knol.google.com/k/simple-simhashing#) .

Thư viện này để so sánh văn bản , hiện đã có implement bằng C .

chạy  sudo ./ext_skel --extname=simhash

để tạo thư mục và template cho extension mới .

cd simhash ( để vào thư mục simhash )

4. chạy sudo phpize để sinh ra các file giúp chung ta có thể build extension độc lập , mà không cần build toàn bộ php5 .

5. Viết code:

Có 2 file cần quan tâm là :

php_simhash :

Bạn phải thêm tên các hàm muốn xây dựng trong extension này :

PHP_MINIT_FUNCTION(simhash);
PHP_MSHUTDOWN_FUNCTION(simhash);
PHP_RINIT_FUNCTION(simhash);
PHP_RSHUTDOWN_FUNCTION(simhash);
PHP_MINFO_FUNCTION(simhash);
PHP_FUNCTION(simhash);
PHP_FUNCTION(confirm_simhash_compiled);

Hàm bôi đậm là mình thêm vào nhé .

File Simhash.c

Khai báo các hàm

const zend_function_entry simhash_functions[] = {
PHP_FE(confirm_simhash_compiled,    NULL)        /* For testing, remove later. */
PHP_FE(simhash, NULL)
{NULL, NULL, NULL}    /* Must be the last line in simhash_functions[] */
};

implement hàm  :

tạm thời cứ print ra Hello, world nhé .

PHP_FUNCTION(simhash)
{
php_printf("Hello, world!\n");
}


6. sudo ./configure

7. sudo make

8. ls modules/

kiểm tra xem có file simhash.so chưa .

Nếu có là ok .

Bạn deploy file simhash.so này bằng cách cấu hình lại php.ini rồi kiểm tra lại bằng phpinfo hoặc tự viết script nhé .

Thật ra trong cái ext của mình cũng có sẵn script test rồi (file simhash.php đấy).

Ngoài ra vấn đề parse các param truyền vào từ php và cách để return giá trị lại cho php caller cũng khá phức tạp .

Để parse param các bạn phải dùng hàm

zend_parse_parameters :


if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a, &b, &return_long) == FAILURE) {
RETURN_NULL();
}


hàm này hoạt động khá giống scanf chỉ khác formate string "ld|b = long - double - và zend_bool .

Cách return về dữ liệu cũng phải thực hiện qua các macro :

Kiều thế này .

RETURN_LONG(a + b);

Thứ Hai, 18 tháng 10, 2010

Change height cell in tableViewCell in iphone by objective C

Viết hai hàm này vào lớp có tableViewCell

- (CGFloat)heightForTweetCellWithString:(NSString *)text {
//change size
return MAX(85, 80);
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

return [self heightForTweetCellWithString:@"change Height"];

}

Thứ Bảy, 16 tháng 10, 2010

Tự động cập nhật svn trên webserver

Mỗi khi các dev commit code lên svn repository , thường phải có động tác ssh lên webserver gõ một câu lệnh buồn tẻ
: svn update :(

Để tránh điều này có thể sử dụng hook script sau :
( hook vào post-commit nhé )


#!/bin/sh
REPOS="$1"
REV="$2"
cd /var/www/youdir/
svn log -vr $REV /var/www/youdir/;
svnlook dirs-changed "$REPOS" -r "$REV" | xargs svn update --non-recursive --username "www-data" --password "") 2>&1) | mail -s "$REPOS@$REV"ngoc_@aiti.com.vn


script sẽ thực hiện update code và gửi email thông báo cho ngoc_@ aiti.com.vn mới cho webserver khi có event commited . Với script này chỉ thực hiện được với các svn hosting nằm cùng server với webserver .

Với trường hợp khác server , sẽ cần một script khác ở remote server viết bằng php hoặc perl .
Cái này bài viết sau mình sẽ viết .

Cài đặt plugin cho phép trac gửi tin qua IRC , email

Cài đặt plugin cho phép trac gửi tin qua IRC và email .

1.Để gửi email chỉ cần config file /etc/trac.ini :

[notification]
smtp_enabled = true
use_tls = true
mime_encoding = base64
smtp_server = smtp.gmail.com
smtp_port = 587
smtp_user = myusername@gmail.com
smtp_password = mypassword


2. Để cài đặt IRC notice bạn phải cài irccat trước ( xem các bài viết trước ) .

Sau đó build plugin

1. svn co http://irccat-listener-trac-plugin.googlecode.com/svn/trunk/
2. wget http://peak.telecommunity.com/dist/ez_setup.py
3.python ez_setup.py
4.cd trunk
5.python setup.py bdist_egg
Bạn sẽ thu được một file egg trong thu mục dist .
Deploy nó vào trong trac bằng trac admin , hoặc copy vào thư mục trac của dự án .

Done .

Dùng Zend_ACL trong CodeIgniter

Qua tìm hiểu thì : Codeigniter không có thư việc cho ACL
Mặt khác cũng không có ACL plugin nào cho CI đủ tốt .

Vì vậy giải pháp dùng Zend_ACL là khả thi nhất .

Zend ACL là một thư viện nhỏ trong Zend Framework . Chúng chỉ có khoảng 4 file .

Rất thích hợp để dưa vào CI .

  1. Download thư viện Zend framework

  2. Tạo thư mục  Zend trong thư mục   /system/application/libraries của CodeIgniter .

  3. Copy  thư mục  Acl , file  Acl.php và file Exception.php của Zend framework vào thư mục Zend vừa tạo .

  4. Tạo file  Zacl.php trong thư mục libraries .<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
    class Zacl
    {
    // Set the instance variable
    var $CI;


    function __construct()
    {
    // Get the instance
    $this->CI =& get_instance();

    // Set the include path and require the needed files
    set_include_path(get_include_path() . PATH_SEPARATOR . BASEPATH . "application/libraries");
    require_once(APPPATH . '/libraries/Zend/Acl.php');
    require_once(APPPATH . '/libraries/Zend/Acl/Role.php');
    require_once(APPPATH . '/libraries/Zend/Acl/Resource.php');
    $this->acl = new Zend_Acl();

    // Set the default ACL
    $this->acl->addRole(new Zend_Acl_Role('default'));
    $query = $this->CI->db->get('tbl_aclresources');
    foreach($query->result() AS $row){
    $this->acl->add(new Zend_Acl_Resource($row->resource));
    if($row->default_value == 'true'){
    $this->acl->allow('default', $row->resource);
    }
    }
    // Get the ACL for the roles
    $this->CI->db->order_by("roleorder", "ASC");
    $query = $this->CI->db->get('tbl_aclroles');
    foreach($query->result() AS $row){
    $role = (string)$row->name;
    $this->acl->addRole(new Zend_Acl_Role($role), 'default');
    $this->CI->db->from('tbl_acl');
    $this->CI->db->join('tbl_aclresources', 'tbl_acl.resource_id = tbl_aclresources.id');
    $this->CI->db->where('type', 'role');
    $this->CI->db->where('type_id', $row->id);
    $subquery = $this->CI->db->get();
    foreach($subquery->result() AS $subrow){
    if($subrow->action == "allow"){
    $this->acl->allow($role, $subrow->resource);
    } else {
    $this->acl->deny($role, $subrow->resource);
    }
    }
    // Get the ACL for the users
    $this->CI->db->from('tbl_users');
    $this->CI->db->where('roleid', $row->id);
    $userquery = $this->CI->db->get();
    foreach($userquery->result() AS $userrow){
    $this->acl->addRole(new Zend_Acl_Role($userrow->user), $role);
    $this->CI->db->from('tbl_acl');
    $this->CI->db->join('tbl_aclresources', 'tbl_acl.resource_id = tbl_aclresources.id');
    $this->CI->db->where('type', 'user');
    $this->CI->db->where('type_id', $userrow->userid);
    $usersubquery = $this->CI->db->get();
    foreach($usersubquery->result() AS $usersubrow){
    if($usersubrow->action == "allow"){
    $this->acl->allow($userrow->user, $usersubrow->resource);
    } else {
    $this->acl->deny($userrow->user, $usersubrow->resource);
    }
    }
    }
    }
    }

    // Function to check if the current or a preset role has access to a resource
    function check_acl($resource, $role = '')
    {
    if (!$this->acl->has($resource))
    {
    return 1;
    }
    if (empty($role)) {
    if (isset($this->CI->session->userdata['user'])) {
    $role = $this->CI->session->userdata['user'];
    }
    }
    if (empty($role)) {
    return false;
    }
    return $this->acl->isAllowed($role, $resource);
    }
    }

  5. Nếu muốn Zacl tự động load vào CI thì code vào trong file autoload , cái này nên cân nhắc kỹ :D .

  6. Cách dùng : $this->zacl->check_acl gọi hàm này :D


7. DB script
CREATE TABLE IF NOT EXISTS `tbl_acl` (
`id` int(11) NOT NULL auto_increment,
`type` enum('role','user') NOT NULL,
`type_id` int(11) NOT NULL,
`resource_id` int(11) NOT NULL,
`action` enum('allow','deny') NOT NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT;

CREATE TABLE IF NOT EXISTS `tbl_aclresources` (
`id` int(11) NOT NULL auto_increment,
`resource` varchar(255) NOT NULL,
`description` longtext NOT NULL,
`aclgroup` varchar(255) NOT NULL,
`aclgrouporder` int(11) NOT NULL,
`default_value` enum('true','false') NOT NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT;

CREATE TABLE IF NOT EXISTS `tbl_aclroles` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`roleorder` int(11) NOT NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT;

INSERT INTO `tbl_aclroles` (`id`, `name`, `roleorder`) VALUES
(1, 'Admin', 1),
(2, 'User', 2),
(3, 'Guest', 3);

CREATE TABLE IF NOT EXISTS `tbl_users` (
`userid` int(11) NOT NULL auto_increment,
`user` longtext NOT NULL,
`pass` longtext NOT NULL,
`firstname` longtext NOT NULL,
`prefix` longtext NOT NULL,
`lastname` longtext NOT NULL,
`gender` enum('m','f') NOT NULL,
`roleid` int(11) NOT NULL,
`mail` longtext NOT NULL,
PRIMARY KEY  (`userid`)
) ENGINE=MyISAM DEFAULT;

Thứ Năm, 14 tháng 10, 2010

Cài đặt hệ thống tự động gửi tin nhắn qua IRC

Dựa trên một ý tưởng từ last.fm trong việc quản lý dự án .

Ý tưởng có thể tóm tắt như sau :

Các dev của một dự án sẽ tham gia vào một kênh IRC chat http://vi.wikipedia.org/wiki/IRC

Qua kênh này trong đổi các vấn đề liên quan đến quá trình dev và test của dự án .

Khi sử dụng svn , mỗi khi một thành viên thay đổi code , và commit lên thì có một con irc bot sẽ tự động gửi các thông báo liên quan đến sự thay đổi này vào kênh IRC chat để team có thể nhanh chóng cập nhật .

Với cách làm này team có thể kết nối với nhau tốt hơn .

1. Cài IRC server .

trên ubuntu 10.10 ( các bản cũ hơn mình chưa test )

Với các dự án open , thì các bạn có thể dùng một public IRC server (và bỏ qua bước này ).
sudo apt-get install  ircd-hybrid

2.Cài đặt irccat
wget http://static.last.fm/rj/irccat.tar.bz2

tar -xvf  irccat.tar.bz2

cd irccat

build

Yêu cầu phài cài đặt sẵn java và ant nhé ( build mà ).

chạy .

sh irccat.sh

sửa lại svn để hook các update mới nhất

sửa file /svn/ten_du_an/hook/post-commit

để bắt thời điểm dev commit , thì irccat tự gửi vào kênh
REPOS="$1"
REV="$2"

#"$REPOS"/hooks/mailer.py commit "$REPOS" $REV "$REPOS"/mailer.conf
LOG=`/usr/bin/svnlook log -r $REV $REPOS`
AUTHOR=`/usr/bin/svnlook author -r $REV $REPOS`
echo "SVN commit by $AUTHOR (r$REV) '$LOG' http://183.91.2.104/trac/bantin.com/changeset/$REV" | netcat -q0 localhost 9999

Cài đặt Apache+Trac+Svn trên ubuntu

Cài đặt Apache+Trac+Svn trên ubuntu

Bản ubuntu mình dùng là 10.10

1. Cài đặt các module cần thiết , bao gồm cả apache bằng apt-get.
sudo apt-get install apache2 libapache2-mod-python libapache2-svn python-setuptools subversion python-subversion

2.

Dùng easy_install của python để cài trac
sudo easy_install http://ftp.edgewall.com/pub/trac/Trac-0.12.tar.gz

Ở  thời điểm này thì bản 0.12 là bản stable .

3. Tạo các thư mục , cấu hình , add user :

sudo mkdir /svn
sudo mkdir /trac
sudo htpasswd -cm /etc/svnauth yourusername
sudo htpasswd -m /etc/svnauth nextusername


+Tạo file /etc/svnaccess
sudo vim /etc/svnaccess

các bạn dùng vi vim nano gì cũng được , localhost thì dùng gedit cũng chả sao .

Nội dung của nó kiểu thế này .
[groups]
developers = yourusername, nextusername
[ / ]
@developers = rw
* = r

mục đích là tạo group và gán quyền
developer thì đọc và ghi
còn * thì chỉ có thể đọc .

+ cấu hình apache
sudo vim /etc/apache2/sites-available/default

thêm vào

   


<Location /svn>

DAV svn
SVNParentPath /svn

AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/svnauth
Require valid-user

AuthzSVNAccessFile /etc/svnaccess
</Location>

<Location /trac>

SetHandler mod_python
PythonHandler trac.web.modpython_frontend
PythonOption TracEnvParentDir /trac
PythonOption TracUriRoot /trac

AuthType Basic
AuthName "Trac"
AuthUserFile /etc/svnauth
Require valid-user

</Location>


Nhớ là trong thẻ nhé .

tạo file trac.ini trong /etc/ nội dung :

/etc/trac.ini
[header_logo]
alt = Logo
height = -1
link =
src = /logo.gif
width = -1

Cái file logo là 1 file bất kỳ để làm logo hiển thị phía trên tay trái cảu trac . cái này cũng không cần thiết lắm .
Nhưng muốn đệp thì phải làm :D .

Viết script để add project mới cho nhanh
#!/usr/bin/perl
$sName = $ARGV[0];
$lName = $ARGV[1];
if ($lName eq "") {
$lName = $sName;
}
$sName =~ tr/A-Z/a-z/;
$path = "sudo svnadmin create /svn/$sName";
system ($path);
$path = "sudo chown -R www-data /svn/$sName";
system ($path);
$path = "sudo trac-admin /trac/$sName initenv '$lName' 'sqlite:db/trac.db' 'svn' '/svn/$sName' --inherit=/etc/trac.ini";
system ($path);
$path = "sudo chown -R www-data /trac/$sName";
system ($path);
$path = "sudo trac-admin /trac/$sName permission add yourusername TRAC_ADMIN permission list yourusername";
system ($path);
print "Done!\n\n";

script này các bạn có thể để thẳng vào /trac
đặt tên là create.pl

chạy

perl /trac/create.pl "ProjectName" "ProjectDESC"

nhớ thay các tham số nhé .

truy cập vào dự án :
- trac : http://localhost/trac/ProjectName
- svn : http://localhost/svn/ProjectName

Done .