ext/pg_connection.c in pg-0.13.2 vs ext/pg_connection.c in pg-0.14.0.pre.351

- old
+ new

@@ -1,8 +1,8 @@ /* * pg_connection.c - PG::Connection class extension - * $Id: pg_connection.c,v 679b1db2b430 2012/02/12 20:50:47 ged $ + * $Id: pg_connection.c,v a3a3177b921c 2012/06/17 17:40:49 ged $ * */ #include "pg.h" @@ -19,22 +19,24 @@ * conn = PG::Connection.open(:dbname => 'test') * res = conn.exec('SELECT $1 AS a, $2 AS b, $3 AS c',[1, 2, nil]) * # Equivalent to: * # res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c') * - * See the PGresult class for information on working with the results of a query. + * See the PG::Result class for information on working with the results of a query. * */ VALUE rb_cPGconn; static PQnoticeReceiver default_notice_receiver = NULL; static PQnoticeProcessor default_notice_processor = NULL; static PGconn *pgconn_check( VALUE ); static VALUE pgconn_finish( VALUE ); +#ifdef M17N_SUPPORTED +static VALUE pgconn_set_default_encoding( VALUE self ); +#endif - /* * Global functions */ /* @@ -162,14 +164,10 @@ pgconn_init(int argc, VALUE *argv, VALUE self) { PGconn *conn = NULL; VALUE conninfo; VALUE error; -#ifdef M17N_SUPPORTED - rb_encoding *enc; - const char *encname; -#endif conninfo = rb_funcall2( rb_cPGconn, rb_intern("parse_connect_args"), argc, argv ); conn = PQconnectdb(StringValuePtr(conninfo)); if(conn == NULL) @@ -183,18 +181,11 @@ rb_iv_set(error, "@connection", self); rb_exc_raise(error); } #ifdef M17N_SUPPORTED - /* If Ruby has its Encoding.default_internal set, set PostgreSQL's client_encoding - * to match */ - if (( enc = rb_default_internal_encoding() )) { - encname = pg_get_rb_encoding_as_pg_encoding( enc ); - if ( PQsetClientEncoding(conn, encname) != 0 ) - rb_warn( "Failed to set the default_internal encoding to %s: '%s'", - encname, PQerrorMessage(conn) ); - } + pgconn_set_default_encoding( self ); #endif if (rb_block_given_p()) { return rb_ensure(rb_yield, self, pgconn_finish, self); } @@ -249,12 +240,45 @@ return rb_ensure( rb_yield, rb_conn, pgconn_finish, rb_conn ); } return rb_conn; } +#ifdef HAVE_PQPING /* * call-seq: + * PG::Connection.ping(connection_hash) -> Fixnum + * PG::Connection.ping(connection_string) -> Fixnum + * PG::Connection.ping(host, port, options, tty, dbname, login, password) -> Fixnum + * + * Check server status. + * + * Returns one of: + * [+PQPING_OK+] + * server is accepting connections + * [+PQPING_REJECT+] + * server is alive but rejecting connections + * [+PQPING_NO_RESPONSE+] + * could not establish connection + * [+PQPING_NO_ATTEMPT+] + * connection not attempted (bad params) + */ +static VALUE +pgconn_s_ping( int argc, VALUE *argv, VALUE klass ) +{ + PGPing ping; + VALUE conninfo; + VALUE error; + + conninfo = rb_funcall2( klass, rb_intern("parse_connect_args"), argc, argv ); + ping = PQping( StringValuePtr(conninfo) ); + + return INT2FIX((int)ping); +} +#endif + +/* + * call-seq: * PG::Connection.conndefaults() -> Array * * Returns an array of hashes. Each hash has the keys: * [+:keyword+] * the name of the option @@ -334,23 +358,10 @@ return rval; } -/* - * call-seq: - * PG::Connection.isthreadsafe() -> Boolean - * - * Returns +true+ if libpq is thread safe, +false+ otherwise. - */ -static VALUE -pgconn_s_isthreadsafe(VALUE self) -{ - UNUSED( self ); - return PQisthreadsafe() ? Qtrue : Qfalse; -} - /************************************************************************** * PG::Connection INSTANCE METHODS **************************************************************************/ /* @@ -731,16 +742,16 @@ /* :TODO: get_ssl */ /* * call-seq: - * conn.exec(sql [, params, result_format ] ) -> PGresult + * conn.exec(sql [, params, result_format ] ) -> PG::Result * conn.exec(sql [, params, result_format ] ) {|pg_result| block } * * Sends SQL query request specified by _sql_ to PostgreSQL. - * Returns a PGresult instance on success. - * On failure, it raises a PGError exception. + * Returns a PG::Result instance on success. + * On failure, it raises a PG::Error. * * +params+ is an optional array of the bind parameters for the SQL query. * Each element of the +params+ array may be either: * a hash of the form: * {:value => String (value of bind parameter) @@ -762,11 +773,11 @@ * * The optional +result_format+ should be 0 for text results, 1 * for binary. * * If the optional code block is given, it will be passed <i>result</i> as an argument, - * and the PGresult object will automatically be cleared when the block terminates. + * and the PG::Result object will automatically be cleared when the block terminates. * In this instance, <code>conn.exec</code> returns the value of the block. */ static VALUE pgconn_exec(int argc, VALUE *argv, VALUE self) { @@ -791,12 +802,12 @@ Check_Type(command, T_STRING); /* If called with no parameters, use PQexec */ if(NIL_P(params)) { result = PQexec(conn, StringValuePtr(command)); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); if (rb_block_given_p()) { return rb_ensure(rb_yield, rb_pgresult, pg_result_clear, rb_pgresult); } return rb_pgresult; } @@ -874,26 +885,26 @@ xfree(paramTypes); xfree(paramValues); xfree(paramLengths); xfree(paramFormats); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); if (rb_block_given_p()) { return rb_ensure(rb_yield, rb_pgresult, pg_result_clear, rb_pgresult); } return rb_pgresult; } /* * call-seq: - * conn.prepare(stmt_name, sql [, param_types ] ) -> PGresult + * conn.prepare(stmt_name, sql [, param_types ] ) -> PG::Result * * Prepares statement _sql_ with name _name_ to be executed later. - * Returns a PGresult instance on success. - * On failure, it raises a PGError exception. + * Returns a PG::Result instance on success. + * On failure, it raises a PG::Error. * * +param_types+ is an optional parameter to specify the Oids of the * types of the parameters. * * If the types are not specified, they will be inferred by PostgreSQL. @@ -937,23 +948,23 @@ result = PQprepare(conn, StringValuePtr(name), StringValuePtr(command), nParams, paramTypes); xfree(paramTypes); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); return rb_pgresult; } /* * call-seq: - * conn.exec_prepared(statement_name [, params, result_format ] ) -> PGresult + * conn.exec_prepared(statement_name [, params, result_format ] ) -> PG::Result * conn.exec_prepared(statement_name [, params, result_format ] ) {|pg_result| block } * * Execute prepared named statement specified by _statement_name_. - * Returns a PGresult instance on success. - * On failure, it raises a PGError exception. + * Returns a PG::Result instance on success. + * On failure, it raises a PG::Error. * * +params+ is an array of the optional bind parameters for the * SQL query. Each element of the +params+ array may be either: * a hash of the form: * {:value => String (value of bind parameter) @@ -968,11 +979,11 @@ * * The optional +result_format+ should be 0 for text results, 1 * for binary. * * If the optional code block is given, it will be passed <i>result</i> as an argument, - * and the PGresult object will automatically be cleared when the block terminates. + * and the PG::Result object will automatically be cleared when the block terminates. * In this instance, <code>conn.exec_prepared</code> returns the value of the block. */ static VALUE pgconn_exec_prepared(int argc, VALUE *argv, VALUE self) { @@ -1061,22 +1072,22 @@ xfree(paramValues); xfree(paramLengths); xfree(paramFormats); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); if (rb_block_given_p()) { return rb_ensure(rb_yield, rb_pgresult, pg_result_clear, rb_pgresult); } return rb_pgresult; } /* * call-seq: - * conn.describe_prepared( statement_name ) -> PGresult + * conn.describe_prepared( statement_name ) -> PG::Result * * Retrieve information about the prepared statement * _statement_name_. */ static VALUE @@ -1092,19 +1103,19 @@ else { Check_Type(stmt_name, T_STRING); stmt = StringValuePtr(stmt_name); } result = PQdescribePrepared(conn, stmt); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); return rb_pgresult; } /* * call-seq: - * conn.describe_portal( portal_name ) -> PGresult + * conn.describe_portal( portal_name ) -> PG::Result * * Retrieve information about the portal _portal_name_. */ static VALUE pgconn_describe_portal(self, stmt_name) @@ -1120,40 +1131,41 @@ else { Check_Type(stmt_name, T_STRING); stmt = StringValuePtr(stmt_name); } result = PQdescribePortal(conn, stmt); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); return rb_pgresult; } /* * call-seq: - * conn.make_empty_pgresult( status ) -> PGresult + * conn.make_empty_pgresult( status ) -> PG::Result * - * Constructs and empty PGresult with status _status_. + * Constructs and empty PG::Result with status _status_. * _status_ may be one of: * * +PGRES_EMPTY_QUERY+ * * +PGRES_COMMAND_OK+ * * +PGRES_TUPLES_OK+ * * +PGRES_COPY_OUT+ * * +PGRES_COPY_IN+ * * +PGRES_BAD_RESPONSE+ * * +PGRES_NONFATAL_ERROR+ * * +PGRES_FATAL_ERROR+ + * * +PGRES_COPY_BOTH+ */ static VALUE pgconn_make_empty_pgresult(VALUE self, VALUE status) { PGresult *result; VALUE rb_pgresult; PGconn *conn = pg_get_pgconn(self); result = PQmakeEmptyPGresult(conn, NUM2INT(status)); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); return rb_pgresult; } /* @@ -1288,17 +1300,83 @@ OBJ_INFECT(ret, str); PQfreemem(to); return ret; } +#ifdef HAVE_PQESCAPELITERAL /* * call-seq: + * conn.escape_literal( str ) -> String + * + * Escape an arbitrary String +str+ as a literal. + */ +static VALUE +pgconn_escape_literal(VALUE self, VALUE string) +{ + PGconn *conn = pg_get_pgconn(self); + char *escaped = NULL; + VALUE error; + VALUE result = Qnil; + + Check_Type(string, T_STRING); + + escaped = PQescapeLiteral(conn, RSTRING_PTR(string), RSTRING_LEN(string)); + if (escaped == NULL) + { + error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn)); + rb_iv_set(error, "@connection", self); + rb_exc_raise(error); + return Qnil; + } + result = rb_str_new2(escaped); + PQfreemem(escaped); + OBJ_INFECT(result, string); + + return result; +} +#endif + +#ifdef HAVE_PQESCAPEIDENTIFIER +/* + * call-seq: + * conn.escape_identifier( str ) -> String + * + * Escape an arbitrary String +str+ as an identifier. + */ +static VALUE +pgconn_escape_identifier(VALUE self, VALUE string) +{ + PGconn *conn = pg_get_pgconn(self); + char *escaped = NULL; + VALUE error; + VALUE result = Qnil; + + Check_Type(string, T_STRING); + + escaped = PQescapeIdentifier(conn, RSTRING_PTR(string), RSTRING_LEN(string)); + if (escaped == NULL) + { + error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn)); + rb_iv_set(error, "@connection", self); + rb_exc_raise(error); + return Qnil; + } + result = rb_str_new2(escaped); + PQfreemem(escaped); + OBJ_INFECT(result, string); + + return result; +} +#endif + +/* + * call-seq: * conn.send_query(sql [, params, result_format ] ) -> nil * * Sends SQL query request specified by _sql_ to PostgreSQL for * asynchronous processing, and immediately returns. - * On failure, it raises a PGError exception. + * On failure, it raises a PG::Error. * * +params+ is an optional array of the bind parameters for the SQL query. * Each element of the +params+ array may be either: * a hash of the form: * {:value => String (value of bind parameter) @@ -1440,11 +1518,11 @@ * call-seq: * conn.send_prepare( stmt_name, sql [, param_types ] ) -> nil * * Prepares statement _sql_ with name _name_ to be executed later. * Sends prepare command asynchronously, and returns immediately. - * On failure, it raises a PGError exception. + * On failure, it raises a PG::Error. * * +param_types+ is an optional parameter to specify the Oids of the * types of the parameters. * * If the types are not specified, they will be inferred by PostgreSQL. @@ -1503,11 +1581,11 @@ * conn.send_query_prepared( statement_name [, params, result_format ] ) * -> nil * * Execute prepared named statement specified by _statement_name_ * asynchronously, and returns immediately. - * On failure, it raises a PGError exception. + * On failure, it raises a PG::Error. * * +params+ is an array of the optional bind parameters for the * SQL query. Each element of the +params+ array may be either: * a hash of the form: * {:value => String (value of bind parameter) @@ -1665,22 +1743,22 @@ } /* * call-seq: - * conn.get_result() -> PGresult + * conn.get_result() -> PG::Result * conn.get_result() {|pg_result| block } * * Blocks waiting for the next result from a call to * #send_query (or another asynchronous command), and returns * it. Returns +nil+ if no more results are available. * * Note: call this function repeatedly until it returns +nil+, or else * you will not be able to issue further commands. * * If the optional code block is given, it will be passed <i>result</i> as an argument, - * and the PGresult object will automatically be cleared when the block terminates. + * and the PG::Result object will automatically be cleared when the block terminates. * In this instance, <code>conn.exec</code> returns the value of the block. */ static VALUE pgconn_get_result(VALUE self) { @@ -1689,11 +1767,11 @@ VALUE rb_pgresult; result = PQgetResult(conn); if(result == NULL) return Qnil; - rb_pgresult = pg_new_result(result, conn); + rb_pgresult = pg_new_result(result, self); if (rb_block_given_p()) { return rb_ensure(rb_yield, rb_pgresult, pg_result_clear, rb_pgresult); } return rb_pgresult; @@ -1795,11 +1873,11 @@ * * Attempts to flush any queued output data to the server. * Returns +true+ if data is successfully flushed, +false+ * if not (can only return +false+ if connection is * nonblocking. - * Raises PGError exception if some other failure occurred. + * Raises PG::Error if some other failure occurred. */ static VALUE pgconn_flush(self) VALUE self; { @@ -2241,11 +2319,11 @@ * printing. However, an application that chooses to provide its own notice * receiver will typically ignore the notice processor layer and just do all * the work in the notice receiver. * * This function takes a new block to act as the handler, which should - * accept a single parameter that will be a PGresult object, and returns + * accept a single parameter that will be a PG::Result object, and returns * the Proc object previously set, or +nil+ if it was previously the default. * * If you pass no arguments, it will reset the handler to the default. */ static VALUE @@ -2385,23 +2463,23 @@ VALUE rb_pgresult; int status; if (rb_block_given_p()) { result = PQexec(conn, "BEGIN"); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); rb_protect(rb_yield, self, &status); if(status == 0) { result = PQexec(conn, "COMMIT"); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); } else { /* exception occurred, ROLLBACK and re-raise */ result = PQexec(conn, "ROLLBACK"); - rb_pgresult = pg_new_result(result, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result(result, self); + pg_result_check(rb_pgresult); rb_jump_tag(status); } } else { @@ -2662,11 +2740,11 @@ #endif /* Win32 */ /* * call-seq: - * conn.get_last_result( ) -> PGresult + * conn.get_last_result( ) -> PG::Result * * This function retrieves all available results * on the current connection (from previously issued * asynchronous commands like +send_query()+) and * returns the last non-NULL result, or +nil+ if no @@ -2695,21 +2773,21 @@ if (status == PGRES_COPY_OUT || status == PGRES_COPY_IN) break; } if (prev) { - rb_pgresult = pg_new_result(prev, conn); - pg_check_result(self, rb_pgresult); + rb_pgresult = pg_new_result( prev, self ); + pg_result_check(rb_pgresult); } return rb_pgresult; } /* * call-seq: - * conn.async_exec(sql [, params, result_format ] ) -> PGresult + * conn.async_exec(sql [, params, result_format ] ) -> PG::Result * conn.async_exec(sql [, params, result_format ] ) {|pg_result| block } * * This function has the same behavior as #exec, * except that it's implemented using asynchronous command * processing and ruby's +rb_thread_select+ in order to @@ -2743,11 +2821,11 @@ /* * call-seq: * conn.lo_creat( [mode] ) -> Fixnum * * Creates a large object with mode _mode_. Returns a large object Oid. - * On failure, it raises PGError exception. + * On failure, it raises PG::Error. */ static VALUE pgconn_locreat(int argc, VALUE *argv, VALUE self) { Oid lo_oid; @@ -2770,11 +2848,11 @@ /* * call-seq: * conn.lo_create( oid ) -> Fixnum * * Creates a large object with oid _oid_. Returns the large object Oid. - * On failure, it raises PGError exception. + * On failure, it raises PG::Error. */ static VALUE pgconn_locreate(VALUE self, VALUE in_lo_oid) { Oid ret, lo_oid; @@ -2792,11 +2870,11 @@ * call-seq: * conn.lo_import(file) -> Fixnum * * Import a file to a large object. Returns a large object Oid. * - * On failure, it raises a PGError exception. + * On failure, it raises a PG::Error. */ static VALUE pgconn_loimport(VALUE self, VALUE filename) { Oid lo_oid; @@ -3105,13 +3183,12 @@ /* * call-seq: * conn.external_encoding() -> Encoding * - * defined in Ruby 1.9 or later. - * - Returns the server_encoding of the connected database as a Ruby Encoding object. - * - Maps 'SQL_ASCII' to ASCII-8BIT. + * Return the +server_encoding+ of the connected database as a Ruby Encoding object. + * The <tt>SQL_ASCII</tt> encoding is mapped to to <tt>ASCII_8BIT</tt>. */ static VALUE pgconn_external_encoding(VALUE self) { PGconn *conn = pg_get_pgconn( self ); @@ -3129,10 +3206,39 @@ rb_iv_set( self, "@external_encoding", encoding ); return encoding; } + + +/* + * call-seq: + * conn.set_default_encoding() -> Encoding + * + * If Ruby has its Encoding.default_internal set, set PostgreSQL's client_encoding + * to match. Returns the new Encoding, or +nil+ if the default internal encoding + * wasn't set. + */ +static VALUE +pgconn_set_default_encoding( VALUE self ) +{ + PGconn *conn = pg_get_pgconn( self ); + rb_encoding *enc; + const char *encname; + + if (( enc = rb_default_internal_encoding() )) { + encname = pg_get_rb_encoding_as_pg_encoding( enc ); + if ( PQsetClientEncoding(conn, encname) != 0 ) + rb_warn( "Failed to set the default_internal encoding to %s: '%s'", + encname, PQerrorMessage(conn) ); + return rb_enc_from_encoding( enc ); + } else { + return Qnil; + } +} + + #endif /* M17N_SUPPORTED */ void @@ -3150,15 +3256,17 @@ SINGLETON_ALIAS(rb_cPGconn, "setdblogin", "new"); rb_define_singleton_method(rb_cPGconn, "escape_string", pgconn_s_escape, 1); SINGLETON_ALIAS(rb_cPGconn, "escape", "escape_string"); rb_define_singleton_method(rb_cPGconn, "escape_bytea", pgconn_s_escape_bytea, 1); rb_define_singleton_method(rb_cPGconn, "unescape_bytea", pgconn_s_unescape_bytea, 1); - rb_define_singleton_method(rb_cPGconn, "isthreadsafe", pgconn_s_isthreadsafe, 0); rb_define_singleton_method(rb_cPGconn, "encrypt_password", pgconn_s_encrypt_password, 2); rb_define_singleton_method(rb_cPGconn, "quote_ident", pgconn_s_quote_ident, 1); rb_define_singleton_method(rb_cPGconn, "connect_start", pgconn_s_connect_start, -1); rb_define_singleton_method(rb_cPGconn, "conndefaults", pgconn_s_conndefaults, 0); +#ifdef HAVE_PQPING + rb_define_singleton_method(rb_cPGconn, "ping", pgconn_s_ping, -1); +#endif /****** PG::Connection INSTANCE METHODS: Connection Control ******/ rb_define_method(rb_cPGconn, "initialize", pgconn_init, -1); rb_define_method(rb_cPGconn, "connect_poll", pgconn_connect_poll, 0); rb_define_method(rb_cPGconn, "finish", pgconn_finish, 0); @@ -3197,10 +3305,16 @@ rb_define_method(rb_cPGconn, "describe_prepared", pgconn_describe_prepared, 1); rb_define_method(rb_cPGconn, "describe_portal", pgconn_describe_portal, 1); rb_define_method(rb_cPGconn, "make_empty_pgresult", pgconn_make_empty_pgresult, 1); rb_define_method(rb_cPGconn, "escape_string", pgconn_s_escape, 1); rb_define_alias(rb_cPGconn, "escape", "escape_string"); +#ifdef HAVE_PQESCAPELITERAL + rb_define_method(rb_cPGconn, "escape_literal", pgconn_escape_literal, 1); +#endif +#ifdef HAVE_PQESCAPEIDENTIFIER + rb_define_method(rb_cPGconn, "escape_identifier", pgconn_escape_identifier, 1); +#endif rb_define_method(rb_cPGconn, "escape_bytea", pgconn_s_escape_bytea, 1); rb_define_method(rb_cPGconn, "unescape_bytea", pgconn_s_unescape_bytea, 1); /****** PG::Connection INSTANCE METHODS: Asynchronous Command Processing ******/ rb_define_method(rb_cPGconn, "send_query", pgconn_send_query, -1); @@ -3279,9 +3393,10 @@ #ifdef M17N_SUPPORTED rb_define_method(rb_cPGconn, "internal_encoding", pgconn_internal_encoding, 0); rb_define_method(rb_cPGconn, "internal_encoding=", pgconn_internal_encoding_set, 1); rb_define_method(rb_cPGconn, "external_encoding", pgconn_external_encoding, 0); + rb_define_method(rb_cPGconn, "set_default_encoding", pgconn_set_default_encoding, 0); #endif /* M17N_SUPPORTED */ }