4/1に間に合わなかったけどSQLite3にPLEASE句を追加するパッチを書きました

5 min read読了の目安(約4500字

はじめに

4/1 に MySQL に please 句を入れるのが流行っていた様です。

https://blog.gmo.media/please-clause-for-mysql-april-fool/

そして各 RDBMS も追従して please 句を入れる祭が開催されていた様です。

まずは PostgreSQL

https://twitter.com/masahiko_sawada/status/1377533102998712327

そして Oracle

https://qiita.com/abe_masanori/items/7cbe45f7db23eee1cf02

完全にノリ遅れてしまいました。大失態です。

気付いたのは 4/4 の昨日です。

これは乗るしかない

既に 4/5 の午前 0 時なのですが、SQLite3 に please 句を入れるパッチを書きました。

diff --git a/src/parse.y b/src/parse.y
index b748e1917..782199fb7 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -151,6 +151,12 @@ ecmd ::= explain cmdx SEMI.       {NEVER-REDUCE}
 explain ::= EXPLAIN.              { pParse->explain = 1; }
 explain ::= EXPLAIN QUERY PLAN.   { pParse->explain = 2; }
 %endif  SQLITE_OMIT_EXPLAIN
+cmdlist ::= cmdlist pcmd.
+cmdlist ::= pcmd.
+%ifndef SQLITE_OMIT_PLEASE
+pcmd ::= please cmdx SEMI.        {NEVER-REDUCE}
+please ::= PLEASE.                { pParse->please = 1; }
+%endif  SQLITE_OMIT_PLEASE
 cmdx ::= cmd.           { sqlite3FinishCoding(pParse); }
 
 ///////////////////// Begin and end transactions. ////////////////////////////
@@ -224,7 +230,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
 // at the beginning.
 //
 %token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST.
-%token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL.
+%token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL PLEASE.
 %token OR AND NOT IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
 %token GT LE LT GE ESCAPE.
 
@@ -237,7 +243,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
   CONFLICT DATABASE DEFERRED DESC DETACH DO
   EACH END EXCLUSIVE EXPLAIN FAIL FOR
   IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
-  QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS
+  QUERY KEY OF OFFSET PLEASE PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS
   ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
   NULLS FIRST LAST
 %ifdef SQLITE_OMIT_COMPOUND_SELECT
diff --git a/src/select.c b/src/select.c
index 7c84bd7d6..1a1898222 100644
--- a/src/select.c
+++ b/src/select.c
@@ -6119,6 +6119,9 @@ int sqlite3Select(
   }
 #endif
 
+  if (pParse->please == 0) {
+    sqlite3OsSleep(db->pVfs, 1000*1000);
+  }
   assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo );
   assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
   assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 783950fb0..6931cb66b 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3485,6 +3485,7 @@ struct Parse {
   ynVar nVar;               /* Number of '?' variables seen in the SQL so far */
   u8 iPkSortOrder;          /* ASC or DESC for INTEGER PRIMARY KEY */
   u8 explain;               /* True if the EXPLAIN flag is found on the query */
+  u8 please;               /* True if the PLEASE flag is found on the query */
   u8 eParseMode;            /* PARSE_MODE_XXX constant */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   int nVtabLock;            /* Number of virtual tables to lock */
diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c
index bbb0ccf29..1909f01a0 100644
--- a/tool/mkkeywordhash.c
+++ b/tool/mkkeywordhash.c
@@ -164,6 +164,11 @@ struct Keyword {
 #else
 #  define RETURNING  0x00400000
 #endif
+#ifdef SQLITE_OMIT_PLEASE
+#  define PLEASE     0
+#else
+#  define PLEASE     0x00800000
+#endif
 
 
 /*
@@ -273,6 +278,7 @@ static Keyword aKeywordTable[] = {
   { "OVER",             "TK_OVER",         WINDOWFUNC,       3      },
   { "PARTITION",        "TK_PARTITION",    WINDOWFUNC,       3      },
   { "PLAN",             "TK_PLAN",         EXPLAIN,          0      },
+  { "PLEASE",           "TK_PLEASE",       PLEASE,           1      },
   { "PRAGMA",           "TK_PRAGMA",       PRAGMA,           0      },
   { "PRECEDING",        "TK_PRECEDING",    WINDOWFUNC,       3      },
   { "PRIMARY",          "TK_PRIMARY",      ALWAYS,           1      },

SQLite3 の拡張は書いた事がありましたが、SQLite3 の本体のパーサのソースを読むのはこれが初めてだったので、まぁまぁ時間を取ってしまいました。ですが適当なパッチではなく、configure のフラグ等にも対応した、ちゃんとしたパッチに仕上げる事ができました。

実際に動かしてみる

SQLite3 のシェルには .timer on があるのでそれを付けて実行します。まずは please を付けない場合。

中二病をこじらせて1秒遅くなります。そして please を付けて実行。

ご機嫌も直って通常のパフォーマンスが得られました。

おわりに

既に 4/5 ですが、遅れて SQLite3 も仲間に入る事ができました。良かったですね。みなさんも SQLite3 を悪魔改造して楽しんで下さい。