まさかあの Google から、この僕にこんな物が届くとは思ってなかった。
仕事に疲れて深夜に帰宅した僕は、テーブルに置かれた郵便物に気がついた。
送り主は Google
大きめの封筒だ。
封筒を開けた瞬間、僕は一瞬引いた。
緑色の何かが見えた。書類ではない。ぬいぐるみでもない。
「やばい」
僕の脳によぎったその感覚は、疲れていた僕の体を一瞬にして変な気分に変えた。
2012/10/07
2012/09/22
SQLite で twitter のタイムラインを select する。
ちょっと前に PostgreSQL に関する記事を見た。最近 PostgreSQL がアツイらしい。
だがしかし SQLite3 でも出来る!きっと出来る!
このソースを
さて...
もちろん
って事で、データベースは MySQL でも PostgreSQL でもなく、SQLite に決まり!
PostgreSQLに興味がある人向けにまとめてみた。|PostgreSQL|お仕事メモ|Pictnotesそれはワクテカ過ぎる...
http://www.pictnotes.jp/memo/archives/6
- Q. FDWってなによ?
- A. FDW(Foreign Data Wrappe),外部データラッパっていうやつで、SQL/MED(Management of External Data)の規格の一つで簡単にいうと、PostgreSQLにQUERYを発行したら、あら不思議、外部の(たとえばMySQLとかCSV)データが取得できるという変態機能。
twitterAPIに変更あるらしいから今後はわからにけど、twitterのデータを取ってくるとかもできる。というかラッパーが用意されてる。(ちなみに使ってみたら楽しかったw)
だがしかし SQLite3 でも出来る!きっと出来る!
#include <string>
#include <sstream>
#include <sqlite3.h>
#include <sqlite3ext.h>
#include <curl/curl.h>
#include "picojson.h"
#ifdef _WIN32
# define EXPORT __declspec(dllexport)
#else
# define EXPORT
#endif
SQLITE_EXTENSION_INIT1;
typedef struct {
char* data; // response data from server
size_t size; // response size of data
} MEMFILE;
MEMFILE*
memfopen() {
MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
if (mf) {
mf->data = NULL;
mf->size = 0;
}
return mf;
}
void
memfclose(MEMFILE* mf) {
if (mf->data) free(mf->data);
free(mf);
}
size_t
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
MEMFILE* mf = (MEMFILE*) stream;
int block = size * nmemb;
if (!mf) return block; // through
if (!mf->data)
mf->data = (char*) malloc(block);
else
mf->data = (char*) realloc(mf->data, mf->size + block);
if (mf->data) {
memcpy(mf->data + mf->size, ptr, block);
mf->size += block;
}
return block;
}
char*
memfstrdup(MEMFILE* mf) {
char* buf;
if (mf->size == 0) return NULL;
buf = (char*) malloc(mf->size + 1);
memcpy(buf, mf->data, mf->size);
buf[mf->size] = 0;
return buf;
}
static int
my_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) {
std::stringstream ss;
ss << "CREATE TABLE " << argv[0]
<< "(id text, screen_name text, tweet text)";
int rc = sqlite3_declare_vtab(db, ss.str().c_str());
*ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
memset(*ppVTab, 0, sizeof(sqlite3_vtab));
return rc;
}
static int
my_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) {
return my_connect(db, pAux, argc, argv, ppVTab, c);
}
static int my_disconnect(sqlite3_vtab *pVTab) {
sqlite3_free(pVTab);
return SQLITE_OK;
}
static int
my_destroy(sqlite3_vtab *pVTab) {
sqlite3_free(pVTab);
return SQLITE_OK;
}
typedef struct {
sqlite3_vtab_cursor base;
int index;
picojson::value* rows;
} cursor;
static int
my_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
MEMFILE* mf;
CURL* curl;
char* json;
mf = memfopen();
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, "http://api.twitter.com/1/statuses/public_timeline.json");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
picojson::value* v = new picojson::value;
std::string err;
picojson::parse(*v, mf->data, mf->data + mf->size, &err);
memfclose(mf);
if (!err.empty()) {
delete v;
std::cerr << err << std::endl;
return SQLITE_FAIL;
}
cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor));
c->rows = v;
c->index = 0;
*ppCursor = &c->base;
return SQLITE_OK;
}
static int
my_close(cursor *c) {
delete c->rows;
sqlite3_free(c);
return SQLITE_OK;
}
static int
my_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
c->index = 0;
return SQLITE_OK;
}
static int
my_next(cursor *c) {
c->index++;
return SQLITE_OK;
}
static int
my_eof(cursor *c) {
return c->index >= c->rows->get<picojson::array>().size() ? 1 : 0;
}
static int
my_column(cursor *c, sqlite3_context *ctxt, int i) {
picojson::value v = c->rows->get<picojson::array>()[c->index];
picojson::object row = v.get<picojson::object>();
const char* p = NULL;
switch (i) {
case 0:
p = row["id"].to_str().c_str();
break;
case 1:
p = row["user"].get<picojson::object>()["screen_name"].to_str().c_str();
break;
case 2:
p = row["text"].to_str().c_str();
break;
}
sqlite3_result_text(ctxt, strdup(p), strlen(p), free);
return SQLITE_OK;
}
static int
my_rowid(cursor *c, sqlite3_int64 *pRowid) {
*pRowid = c->index;
return SQLITE_OK;
}
static int
my_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
return SQLITE_OK;
}
static const sqlite3_module module = {
0,
my_create,
my_connect,
my_bestindex,
my_disconnect,
my_destroy,
my_open,
(int (*)(sqlite3_vtab_cursor *)) my_close,
(int (*)(sqlite3_vtab_cursor *, int, char const *, int, sqlite3_value **)) my_filter,
(int (*)(sqlite3_vtab_cursor *)) my_next,
(int (*)(sqlite3_vtab_cursor *)) my_eof,
(int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) my_column,
(int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) my_rowid,
NULL, // my_update
NULL, // my_begin
NULL, // my_sync
NULL, // my_commit
NULL, // my_rollback
NULL, // my_findfunction
NULL, // my_rename
};
static void
destructor(void *arg) {
return;
}
extern "C" {
EXPORT int
sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
SQLITE_EXTENSION_INIT2(api);
sqlite3_create_module_v2(db, "twitter_public_timeline", &module, NULL, destructor);
return 0;
}
}
picojson を使うので適当に持ってきて下さい。このソースを
sqlite3_twit.cxx として
$ g++ -I. -g -o sqlite3_twit.dll -shared sqlite3_twit.cxx -lcurldll
の様にコンパイルする。unix な人は... 調べて下さい。さて...
$ sqlite3
SQLite version 3.7.14 2012-09-03 15:42:36
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
SQLiteのシェルに入ったら
sqlite> select load_extension("sqlite3_twit.dll");
load_extension("sqlite3_twit.dll")
----------------------------------
拡張をロードして
sqlite> create virtual table tbl using twitter_public_timeline(id, screen_name, text);
仮想テーブルを作る。あとは...
sqlite> select * from tbl;
id screen_name tweet
------------------ ------------ ---------------------------------------------------------------------------------------------
248056468924928000 sosomustafa2 الحياة بسيطه جدا لدرجة أن الابتسامه قد تجعلك محبوب جدا ♡"
248056468727808000 nurulauliad Sekalinya dateng, dateng semua. Sekalinya pergi, pergi semua. Itu lah sebab kenapa hidup penuh pilihan.
248056468421632000 ZaldivarSkat ¡HOY HOY HOY! Extreno de O.N.I.F.C de @RealWizKhalifa
248056468291584000 chimpungdila Ciye! "@putuderism: Hey 1530 QL, i miss you so badly :'))"
248056467767296000 44Velboo 268とかプロボウラー並の人がいる(^_^;)
248056467154944000 kevintumongg Karepmu her RT @KurniawanHerdi: Mau banget ga mong? RT @kevintumonggi: @KurniawanHerdi folbeck her
248056467045888000 rnbanyu Detik detik
248056466739712000 iiiFoN [HD] The Voice Thailand : Week2 (Full Version) 16 Sep 2012: http://t.co/mRIaKDuy via @youtube
248056465582080000 CarbonQ8e ياكـــم نفـــسٍ عـــن هـــوا الحـــب طابـــت
248056465166848000 WhoIsVARRI_ I'm just Boolin how i B Boolin !
248056463069696000 iSwiftCyrus Photo: http://t.co/qAcDMm1c
248056463048704000 IGORNOKALT Baixei o ringtone do gummy bear :9
248056462130176000 meowkikocel #MentionSomeoneVerySpecial @Mistacey :)
248056462109184000 xFatehx just got back from NVBA , phhewww what a great match :D
248056461912064000 snags17 「倭」を日本の蔑称として使いたがるのは、「現代では」、中国人より韓国<e4><ba>
248056461803008000 hendraMirvan RT @gadasianipar: RT @febrinacitra: Ket biyen nonton sketsa kok rak tau ngguyu blas to.... (‾▿‾")
248056460972544000 _Azrie_ begitu lah gaya nya , online , tapi hanya memerhatikan TL.
248056460557312000 imbaked_ This one of those morning were I smoke a blunt nd watch Kevin Hart
248056459814912000 psanso + RT“@damiaborras: El rei diu que hem de remar tots… Deu tenir el Fortuna espatllat.”
sqlite>
キターーー!もちろん
sqlite> select * from tbl where tweet like '%twitter%';
id screen_name tweet
------------------ ------------ -----------
248059202780672000 Baby_Nandos_ New Twitter
where区だって使えるんだぜ!public timeline速過ぎるから毎回違うテーブル内容なんだぜ!って事で、データベースは MySQL でも PostgreSQL でもなく、SQLite に決まり!
バッチ ジョブを終了しますか (Y/N)?
ハッカーのたのしみ: 本物のプログラマはいかにして問題を解くかヘンリー S.ウォーレン ジュニア, 滝沢 徹
エスアイビー・アクセス 単行本 / ¥5,141 (2004年09月01日)
発送可能時間:
Windows を UNIX っぽく使おうとすると、必ずお目にかかるであろうこのメッセージ。UNIX に慣れている人は、コマンドプロンプトを使いたがる。しかし全ての場合においてコンソールアプリケーションは有能では無いし、異常に長い引数は省略したい。Java VM で動く言語のクラスパス等はスクリプトに書きたいし、オフィシャルから Windows ユーザ向けに用意される物はだいたいバッチファイルだ。しかしながら Cygwin は標準提供物ではないし、嫌いだ。いや、大嫌いだ。
そこで私達は一般的に、バッチファイルという一見便利そげで実は非情なまでに我々に独特の仕様を強要するDSLを頻繁に使う事になる。
例えば Java VM 上で動く clojure を Windows 上で repl として使う場合、僕は以下の様なバッチファイルを「clj.bat」というファイル名にして使っている。
@echo off
set CLOJURE_EXT=%USERPROFILE%\.clojure.d
setlocal enabledelayedexpansion
if defined CLOJURE_EXT for %%E in ("%CLOJURE_EXT%\*") do set CP=!CP!;%%~fE
if not defined CLOJURE_JAVA set CLOJURE_JAVA=java
if exist .clojure for /F %%E in (".clojure\*") do set CP=!CP!;%%~fE
%CLOJURE_JAVA% %CLOJURE_OPTS% -cp "%CP%" clojure.main -i "%CLOJURE_EXT%\cljrc.clj" %1 %2 %3 %4 %5 %6 %7 %8 %9 --repl
これは別に大した物ではないし、僕にとって心地よく動作している。しかしこの repl を終了しようと、UNIX ユーザでは当たり前の呪文、そう「CTRL-C」をタイプすると事態が急転する。
C:\temp>clj.bat
Clojure 1.4.0
user=>
バッチ ジョブを終了しますか (Y/N)?
お前は俺が終了しろと言ったのが聞こえないのか?java は既に終了してしまってるのにこれ以上バッチファイルの実行を継続する理由など無い! start を絡ませればメッセージを出さなくする事も出来るが、その start を含んだコマンドをバッチファイルに書いてしまったら堂々巡りだ!我々はコマンドプロンプトからコマンド名で実行したんだ!
全世界の Windows ユーザがこの問題に悩んでいる。
How can I suppress the "terminate batch job" in cmd.exe - Stack Overflow中には cmd.exe にバイナリパッチを当てる人までいる。
I'm looking for a mechanism for suppressing the "Terminate batch job? (Y/N)" invitation that I get whenever I press CTRL-C in a program started from a batch file...
http://stackoverflow.com/questions/1234571/how-can-i-suppress-the-terminate-batch-job-in-cmd-exe
several useful patches for cmd.exe本来、CTRL-C をタイプしてプロセスを終了するという自由な行動が、非人道的な cmd.exe の仕様により不自由な操作を強要されている。(© EzoeRyou)
how to find what to patch using a recent version of IDA, it is quite easy to find where to patch cmd...
http://itsme.home.xs4all.nl/projects/misc/patching-cmdexe.html
これを解決しようと色々考えたが、以下の方法が良いのではないかと思う。
コマンドプロンプトをシェルの様に使う人はフルパスをタイプしないし、拡張子までタイプしない。例えば既述の「clj.bat」であれば「clj」とだけタイプする。拡張子を明示しない場合、Windows では PATHEXT 環境変数を ; で区切った順番に実行ファイルが探索される。通常、PATHEXT では「.bat」よりも「.exe」の方が先に来る。つまり「clj.bat」と同じ位置に「clj.exe」があれば、「clj.exe」が先に見つけられ実行される事になるのだ。
であれば、その exe からバッチファイルを起動し、プロセスグループに加えてやれば CTRL-C が伝播するはず。なお且つ対話形式で起動しない様にすれば「バッチ ジョブを終了しますか (Y/N)?」というふざけたメッセージをお目にかかる事はない。
よろしい、ならばコーディングだ。一番いい Vim を頼む。
#include <windows.h>
#include <string.h>
#include <stdio.h>
int
emsg() {
char* p = NULL;
DWORD err = GetLastError();
if (err == 0) return 0;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, 0, (LPTSTR)(&p), 0, NULL);
fputs(p, stderr);
LocalFree((LPVOID)p);
return err;
}
int
main(int argc, char* argv[]) {
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
int r;
char *p, *t;
t = p = strdup(GetCommandLine());
if (!p) return -1;
if (*p == '"')
do p++; while (*p && *p != '"');
else
while (*p && *p != ' ') p++;
if (!strncasecmp(p-4, ".exe", 4))
memcpy(p-4, ".bat", 4);
else {
char *comspec = getenv("COMSPEC"), *b;
if (!comspec) comspec = "CMD";
b = malloc(strlen(comspec) + 1 + 9 + strlen(t) + 5);
*b = 0;
strcat(b, comspec);
strcat(b, " /c call ");
strncat(b, t, (int) (p-t));
strcat(b, ".bat");
strcat(b, p);
free(t);
t = b;
}
si.cb = sizeof(si);
if (!CreateProcess(
NULL, t, NULL, NULL, TRUE,
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP,
NULL, NULL, &si, &pi))
r = emsg();
else {
CloseHandle(pi.hThread);
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
r = emsg();
else {
DWORD code = 0;
if (!GetExitCodeProcess(pi.hProcess, &code))
r = emsg();
else {
r = (int) code;
CloseHandle(pi.hProcess);
}
}
}
free(t);
return r;
}
ソースファイルは「batter.c」として保存し、コンパイルする。コマンドラインの第一引数に「.exe」が付いていたら「.bat」に書き換え、付いていなかったら付け足し、CREATE_NEW_PROCESS_GROUP を指定してプロセスを起動する。
これをコンパイルして出来上がった batter.exe を、例えば「clj.bat」が置いてあるフォルダに「clj.exe」というファイル名てコピーする。
では新しいコマンドプロンプトを起動し、「clj」とタイプしよう...
C:\temp>clj
Clojure 1.4.0
user=>
そして CTRL-C だ。
^C
C:\temp>
やった。俺は「バッチ ジョブを終了しますか (Y/N)?」メッセージを倒した。今後「clj.bat」を好き放題に編集しても構わない。
俺は勝った。
新標準SQLite (オープンソースRDBMSシリーズ)

