/* * ps_call.c - demonstrate how to use prepared statement support for CALL. */ #include #include #include /* for strdup() */ #include #include /* @# _OPTION_ENUM_ */ #ifdef HAVE_OPENSSL enum options_client { OPT_SSL_SSL=256, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_SSL_VERIFY_SERVER_CERT }; #endif /* @# _OPTION_ENUM_ */ static char *opt_host_name = NULL; /* server host (default=localhost) */ static char *opt_user_name = NULL; /* username (default=login name) */ static char *opt_password = NULL; /* password (default=none) */ static unsigned int opt_port_num = 0; /* port number (use built-in value) */ static char *opt_socket_name = NULL; /* socket name (use built-in value) */ static char *opt_db_name = NULL; /* database name (default=none) */ static unsigned int opt_flags = 0; /* connection flags (none) */ #include static int ask_password = 0; /* whether to solicit password */ static MYSQL *mysql; /* pointer to connection handler */ static const char *client_groups[] = { "client", NULL }; /* #@ _MY_OPTS_ */ static struct my_option my_opts[] = /* option information structures */ { {"help", '?', "Display this help and exit", NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Host to connect to", (uchar **) &opt_host_name, NULL, NULL, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"password", 'p', "Password", (uchar **) &opt_password, NULL, NULL, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "Port number", (uchar **) &opt_port_num, NULL, NULL, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket path", (uchar **) &opt_socket_name, NULL, NULL, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"user", 'u', "User name", (uchar **) &opt_user_name, NULL, NULL, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #include { NULL, 0, NULL, NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 } }; /* #@ _MY_OPTS_ */ static void print_error (MYSQL *mysql, char *message) { fprintf (stderr, "%s\n", message); if (mysql != NULL) { fprintf (stderr, "Error %u (%s): %s\n", mysql_errno (mysql), mysql_sqlstate (mysql), mysql_error (mysql)); } } /* #@ _PRINT_STMT_ERROR_ */ static void print_stmt_error (MYSQL_STMT *stmt, char *message) { fprintf (stderr, "%s\n", message); if (stmt != NULL) { fprintf (stderr, "Error %u (%s): %s\n", mysql_stmt_errno (stmt), mysql_stmt_sqlstate (stmt), mysql_stmt_error (stmt)); } } /* #@ _PRINT_STMT_ERROR_ */ /* #@ _GET_ONE_OPTION_ */ static my_bool get_one_option (int optid, const struct my_option *opt, char *argument) { switch (optid) { case '?': my_print_help (my_opts); /* print help message */ exit (0); case 'p': /* password */ if (!argument) /* no value given; solicit it later */ ask_password = 1; else /* copy password, overwrite original */ { opt_password = strdup (argument); if (opt_password == NULL) { print_error (NULL, "could not allocate password buffer"); exit (1); } while (*argument) *argument++ = 'x'; ask_password = 0; } break; #include } return (0); } /* #@ _GET_ONE_OPTION_ */ /* For simplicity, the code assumes all parameters have INT type (and thus buffer type MYSQL_TYPE_LONG). NOTE: The code uses the MYSQL structure server_status member, which possibly should be considered private. Differences from mysql_client_test.c:test_wl4435() code for prepared CALL: Does not assume that the stored procedure returns any result sets (either produced by the procedure itself or as a result of OUT/INOUT parameters). For this reason it checks the column count first before trying to get result set metadata. No longer uses mysql_num_fields with the metadata to determine the column count. That doesn't work for the final result packet: No metadata is available if there is no result set and mysql_stmt_result_metadata() crashes. mysql_stmt_field_count() is used instead, which returns the same value as mysql_num_fields() if there is a result set, and returns 0 without crashing if there is no result set. Row-printing code allows for the possibility of NULL values. This is because the first result set prints the initial parameter values, and the value of any OUT paremeter will be seen as NULL by the procedure until assigned a value within the procedure. If mysql_stmt_next_result() fails, the error is displayed using mysql_stmt_{error,errno}(stmt), not mysql_{error,errno}(mysql). */ static void test_error(MYSQL *mysql, int status) { if (status) { printf("Error: %s (errno: %d)\n", mysql_error(mysql), mysql_errno(mysql)); exit(1); } } static void test_stmt_error(MYSQL_STMT *stmt, int status) { if (status) { printf("Error: %s (errno: %d)\n", mysql_stmt_error(stmt), mysql_stmt_errno(stmt)); exit(1); } } static void test_call(MYSQL *mysql) { MYSQL_STMT *stmt; MYSQL_BIND ps_params[3]; /* input parameter buffers */ int int_data[3]; /* input parameter values */ my_bool is_null[3]; /* input parameter nullability */ int status; /* set up stored procedure */ status = mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); test_error(mysql, status); status = mysql_query(mysql, "CREATE PROCEDURE p1(" " IN p_in INT, " " OUT p_out INT, " " INOUT p_inout INT) " "BEGIN " " SELECT p_in, p_out, p_inout; " " SET p_in = 100, p_out = 200, p_inout = 300; " " SELECT p_in, p_out, p_inout; " "END"); test_error(mysql, status); /* initialize and prepare CALL statement with parameter placeholders */ stmt = mysql_stmt_init(mysql); if (!stmt) { printf("Could not initialize statement\n"); exit(1); } status = mysql_stmt_prepare(stmt, "CALL p1(?, ?, ?)", 16); test_stmt_error(stmt, status); /* initialize parameters: p_in, p_out, p_inout (all INT) */ memset(ps_params, 0, sizeof (ps_params)); ps_params[0].buffer_type = MYSQL_TYPE_LONG; ps_params[0].buffer = (char *) &int_data[0]; ps_params[0].length = 0; ps_params[0].is_null = 0; ps_params[1].buffer_type = MYSQL_TYPE_LONG; ps_params[1].buffer = (char *) &int_data[1]; ps_params[1].length = 0; ps_params[1].is_null = 0; ps_params[2].buffer_type = MYSQL_TYPE_LONG; ps_params[2].buffer = (char *) &int_data[2]; ps_params[2].length = 0; ps_params[2].is_null = 0; /* bind parameters */ status = mysql_stmt_bind_param(stmt, ps_params); test_stmt_error(stmt, status); /* assign values to parameters and execute statement */ int_data[0]= 10; /* p_in */ int_data[1]= 20; /* p_inout */ int_data[2]= 30; /* p_inout */ status = mysql_stmt_execute(stmt); test_stmt_error(stmt, status); /* process results until there are no more */ do { int i; int num_fields; /* number of columns in result */ MYSQL_FIELD *fields; /* for result set metadata */ MYSQL_BIND *rs_bind; /* for output buffers */ /* the column count is > 0 if there is a result set */ /* 0 if the result is only the final status packet */ num_fields = mysql_stmt_field_count(stmt); if (num_fields > 0) { /* there is a result set to fetch */ printf("Number of columns in result: %d\n", (int) num_fields); /* what kind of result set is this? */ printf("Data: "); if(mysql->server_status & SERVER_PS_OUT_PARAMS) printf("this result set contains OUT/INOUT parameters\n"); else printf("this result set is produced by the procedure\n"); MYSQL_RES *rs_metadata = mysql_stmt_result_metadata(stmt); test_stmt_error(stmt, rs_metadata == NULL); fields = mysql_fetch_fields(rs_metadata); rs_bind = (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields); if (!rs_bind) { printf("Cannot allocate output buffers\n"); exit(1); } memset(rs_bind, 0, sizeof (MYSQL_BIND) * num_fields); /* set up and bind result set output buffers */ for (i = 0; i < num_fields; ++i) { rs_bind[i].buffer_type = fields[i].type; rs_bind[i].is_null = &is_null[i]; switch (fields[i].type) { case MYSQL_TYPE_LONG: rs_bind[i].buffer = (char *) &(int_data[i]); rs_bind[i].buffer_length = sizeof (int_data); break; default: fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type); exit(1); } } status = mysql_stmt_bind_result(stmt, rs_bind); test_stmt_error(stmt, status); /* fetch and display result set rows */ while (1) { status = mysql_stmt_fetch(stmt); if (status == 1 || status == MYSQL_NO_DATA) break; for (i = 0; i < num_fields; ++i) { switch (rs_bind[i].buffer_type) { case MYSQL_TYPE_LONG: if (*rs_bind[i].is_null) printf(" val[%d] = NULL;", i); else printf(" val[%d] = %ld;", i, (long) *((int *) rs_bind[i].buffer)); break; default: printf(" unexpected type (%d)\n", rs_bind[i].buffer_type); } } printf("\n"); } mysql_free_result(rs_metadata); /* free metadata */ free(rs_bind); /* free output buffers */ } else { /* no columns = final status packet */ printf("End of procedure output\n"); } /* more results? -1 = no, >0 = error, 0 = yes (keep looking) */ status = mysql_stmt_next_result(stmt); if (status > 0) test_stmt_error(stmt, status); } while (status == 0); mysql_stmt_close(stmt); } int main (int argc, char *argv[]) { int opt_err; int ps_out_support; MY_INIT (argv[0]); load_defaults ("my", client_groups, &argc, &argv); if ((opt_err = handle_options (&argc, &argv, my_opts, get_one_option))) exit (opt_err); /* solicit password if necessary */ if (ask_password) opt_password = get_tty_password (NULL); /* get database name if present on command line */ if (argc > 0) { opt_db_name = argv[0]; --argc; ++argv; } /* initialize client library */ if (mysql_library_init (0, NULL, NULL)) { print_error (NULL, "mysql_library_init() failed"); exit (1); } /* initialize connection handler */ mysql = mysql_init (NULL); if (mysql == NULL) { print_error (NULL, "mysql_init() failed (probably out of memory)"); exit (1); } #ifdef HAVE_OPENSSL /* pass SSL information to client library */ if (opt_use_ssl) mysql_ssl_set (mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #if (MYSQL_VERSION_ID >= 50023 && MYSQL_VERSION_ID < 50100) \ || MYSQL_VERSION_ID >= 50111 mysql_options (mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); #endif #endif /* connect to server */ if (mysql_real_connect (mysql, opt_host_name, opt_user_name, opt_password, opt_db_name, opt_port_num, opt_socket_name, opt_flags) == NULL) { print_error (mysql, "mysql_real_connect() failed"); mysql_close (mysql); exit (1); } if (mysql_get_server_version(mysql) < 60008) { fprintf(stderr, "Server does not support required CALL capabilities\n"); mysql_close(mysql); exit (1); } test_call (mysql); /* disconnect from server, terminate client library */ mysql_close (mysql); mysql_library_end (); exit (0); }