quote_ident(string text)
- PGconn#quote_ident の有無 - Rubyとか Illustratorとか SFとか折紙とか
- PGconn#quote_ident - Rubyとか Illustratorとか SFとか折紙とか
続き
そもそも quote_ident() って何なんだろうか。7.3あたりで PostgreSQL自体に導入された文字列関数ということでいいのかな(http://www.postgresql.jp/document/pg732doc/user/functions-string.html)。quote_literal() も忘れずに。
http://www.postgresql.jp/document/pg732doc/user/functions-string.html
関数 返り値型 説明 例 結果 quote_ident(string text) text (テキスト) 与えられた文字列を、SQL 問い合わせ文字列で識別子として使用できるように、適切な引用符を付けて返します。 引用符は、必要な場合 (たとえば、文字列に識別子のない文字が含まれる場合や、大文字変換される場合) にのみ追加されます。 埋め込まれた引用符は、適切に二重になります。 quote_ident('Foo') "Foo"
ident は identifier の意か、SQLの識別子を場合によって「"(二重引用符)」で囲って返すべきもの。
その識別子なんだけど、4.1.1. 識別子とキーワード
基本的には英小文字アンダースコア始まりで続く文字には数字も可。というか文字的には非ラテン文字可なので日本語も可。
あと、「"(二重引用符)」で囲ってある系の文字列も可、SQLキーワードと被るときなんか良いよね、「"select"」とか。という辺りで大文字小文字の区別とか空白文字アンパサンド記号とかの便があり、「"(二重引用符)」自体を含むのも可(そのときは二つ続けて書く)
そういうのに対応して文字列を PostgreSQLに与えるべき(引用符付き)識別子(或は"安全な"もの)に変換するのが quote_ident() 文字列関数。だから誤解のないような英小文字だけ文字列とかなら儘、「"」で囲ったりはしない。
ただ、Ruby側のポスグレドライバはこの PostgreSQL の quote_ident()関数を呼んでる様でもないし、そこまで正確をこれを再現してる程でもない様だ。
postgresジェムの quote_ident
gems\1.8\gems\postgres-0.7.9.2008.01.28\ext\postgres.c, ll.385-417
/* * call-seq: * PGconn.quote_ident( str ) * * Returns a SQL-safe identifier. */ static VALUE pgconn_s_quote_ident(self, string) VALUE self; VALUE string; { char *str,*ptr; int i,j=0,len; VALUE result; Check_Type(string, T_STRING); ptr = RSTRING_PTR(string); len = RSTRING_LEN(string); str = ALLOCA_N(char, len * 2 + 2 + 1); str[j++] = '"'; for(i = 0; i < len; i++) { if(ptr[i] == '"') str[j++] = '"'; else if(ptr[i] == '\0') rb_raise(rb_ePGError, "Identifier cannot contain NULL bytes"); str[j++] = ptr[i]; } str[j++] = '"'; result = rb_str_new(str, j); OBJ_INFECT(result, string); return result; }
libpgsql-ruby の quote_ident
libpgsql-ruby_0.7.9.2008.03.18.orig/ruby-pg/ext/pg.c, ll.2275-2320
/* * call-seq: * PGconn.quote_ident( str ) -> String * conn.quote_ident( str ) -> String * * Returns a string that is safe for inclusion in a SQL query * as an identifier. Note: this is not a quote function for values, * but for identifiers. * * For example, in a typical SQL query: +SELECT FOO FROM MYTABLE+ * The identifier +FOO+ is folded to lower case, so it actually means * +foo+. If you really want to access the case-sensitive field name * +FOO+, use this function like +PGconn.quote_ident('FOO')+, which * will return +"FOO"+ (with double-quotes). PostgreSQL will see the * double-quotes, and it will not fold to lower case. * * Similarly, this function also protects against special characters, * and other things that might allow SQL injection if the identifier * comes from an untrusted source. */ static VALUE pgconn_s_quote_ident(VALUE self, VALUE in_str) { VALUE ret; char *str = StringValuePtr(in_str); /* result size at most NAMEDATALEN*2 plus surrounding * double-quotes. */ char buffer[NAMEDATALEN*2+2]; unsigned int i=0,j=0; if(strlen(str) >= NAMEDATALEN) { rb_raise(rb_eArgError, "Input string is longer than NAMEDATALEN-1 (%d)", NAMEDATALEN-1); } buffer[j++] = '"'; for(i = 0; i < strlen(str) && str[i]; i++) { if(str[i] == '"') buffer[j++] = '"'; buffer[j++] = str[i]; } buffer[j++] = '"'; ret = rb_str_new(buffer,j); OBJ_INFECT(ret, in_str); return ret; }
ふたつの PostgreSQLドライバの quote_ident
両者で、確保するメモリ領域に若干の方針の違いと同じ所(+2)を感じたり、エラーを気遣う所が違ったり(文字列中に¥0が入ってるか/文字列が長過ぎないか)だけど。必ず先頭と末尾に「"(二重引用符)」をつけて囲い、文字列中に「"(二重引用符)」が出てくるかどうか一文字(バイト)ずつチェックして出てきてたら「"」をもう一つ補う、という基本的な流れは同し。
そしてコメントにもちょっと説明がある、むしろ大文字小文字の別に注目してるかな。
「'"' + name + '"'」
その意味では前回とかの「'"' + name + '"'」はちょっと不十分だったか。ActiveRecord 2.3.3 以前の「%("#{name}")」と同等ではある。
「'"' + name.gsub('"', '""') + '"'」かな。
或は頑張って PostgreSQLサーバに connectしてその quote_ident()関数を呼び出すとか、頑張れ。