/* * success, event = stmt:prepare_start(query) */ static int statement_prepare_start (lua_State * L) /* {{{ */ { statement_t *statement = (statement_t *) luaL_checkudata (L, 1, DBD_MYSQL_STATEMENT); const char *sql_query = luaL_checkstring (L, 2); unsigned long sql_len = strlen (sql_query); if (!statement->stmt) { luaL_error (L, DBI_ERR_INVALID_STATEMENT); } statement->event = mysql_stmt_prepare_start (&(statement->ret), statement->stmt, sql_query, sql_len); if (statement->event & MYSQL_WAIT_EXCEPT) { lua_pushnil (L); lua_pushfstring (L, DBI_ERR_PREP_STATEMENT, mysql_stmt_error (statement->stmt)); return 2; } if (statement->event & MYSQL_WAIT_TIMEOUT) { statement->timeout = 1000 * mysql_get_timeout_value_ms (statement->mysql); } lua_pushboolean (L, 1); lua_pushnumber (L, convert_mysql_to_ev (statement->event)); return 2; } /* }}} */
/* * success,err = statement:execute_cont(...) */ static int statement_execute_cont (lua_State * L) /* {{{ */ { statement_t *statement = (statement_t *) luaL_checkudata (L, 1, DBD_MYSQL_STATEMENT); int event = convert_ev_to_mysql (lua_tonumber (L, 2)); MYSQL_RES *metadata = NULL; if (!statement->stmt) { luaL_error (L, DBI_ERR_INVALID_STATEMENT); } statement->event = mysql_stmt_execute_cont (&(statement->ret), statement->stmt, event); if (statement->event & MYSQL_WAIT_EXCEPT) { lua_pushnil (L); lua_pushfstring (L, DBI_ERR_BINDING_EXEC, mysql_stmt_error (statement->stmt)); } if (statement->event & MYSQL_WAIT_TIMEOUT) { statement->timeout = 1000 * mysql_get_timeout_value_ms (statement->mysql); } if (statement->ret) { lua_pushnil (L); lua_pushfstring (L, DBI_ERR_BINDING_EXEC, mysql_stmt_error (statement->stmt)); return 2; } if (statement->event > 0) { lua_pushboolean (L, 0); lua_pushnumber (L, convert_mysql_to_ev (statement->event)); return 2; } metadata = mysql_stmt_result_metadata (statement->stmt); statement->metadata = metadata; lua_pushboolean (L, 1); return 1; } /* }}} */
static int wait_for_mysql(MYSQL *mysql, int status) { #ifdef _WIN32 fd_set rs, ws, es; int res; struct timeval tv, *timeout; my_socket s= mysql_get_socket(mysql); FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es); if (status & MYSQL_WAIT_READ) FD_SET(s, &rs); if (status & MYSQL_WAIT_WRITE) FD_SET(s, &ws); if (status & MYSQL_WAIT_EXCEPT) FD_SET(s, &es); if (status & MYSQL_WAIT_TIMEOUT) { tv.tv_sec= mysql_get_timeout_value(mysql); tv.tv_usec= 0; timeout= &tv; } else timeout= NULL; res= select(1, &rs, &ws, &es, timeout); if (res == 0) return MYSQL_WAIT_TIMEOUT; else if (res == SOCKET_ERROR) { /* In a real event framework, we should handle errors and re-try the select. */ return MYSQL_WAIT_TIMEOUT; } else { int status= 0; if (FD_ISSET(s, &rs)) status|= MYSQL_WAIT_READ; if (FD_ISSET(s, &ws)) status|= MYSQL_WAIT_WRITE; if (FD_ISSET(s, &es)) status|= MYSQL_WAIT_EXCEPT; return status; } #else struct pollfd pfd; int timeout; int res= -1; pfd.fd= mysql_get_socket(mysql); pfd.events= (status & MYSQL_WAIT_READ ? POLLIN : 0) | (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) | (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0); if (status & MYSQL_WAIT_TIMEOUT) timeout= mysql_get_timeout_value_ms(mysql); else timeout= -1; do { res= poll(&pfd, 1, timeout); } while (res == -1 && errno == EINTR); if (res == 0) return MYSQL_WAIT_TIMEOUT; else if (res < 0) { /* In a real event framework, we should handle EINTR and re-try the poll. */ return MYSQL_WAIT_TIMEOUT; } else { int status= 0; if (pfd.revents & POLLIN) status|= MYSQL_WAIT_READ; if (pfd.revents & POLLOUT) status|= MYSQL_WAIT_WRITE; if (pfd.revents & POLLPRI) status|= MYSQL_WAIT_EXCEPT; return status; } #endif }
static int statement_fetch_impl_cont (lua_State * L, statement_t * statement, /* {{{ */ int event, int named_columns) { int column_count; unsigned long *real_length = NULL; int values = 0; column_count = mysql_num_fields (statement->metadata); if (column_count > 0) { int i; real_length = calloc (column_count, sizeof (unsigned long)); statement->event = mysql_stmt_fetch_cont (&(statement->ret), statement->stmt, event); if (statement->event & MYSQL_WAIT_EXCEPT) { lua_pushnil (L); lua_pushfstring (L, mysql_stmt_error (statement->stmt)); values = 2; } if (statement->event & MYSQL_WAIT_TIMEOUT) { statement->timeout = 1000 * mysql_get_timeout_value_ms (statement->mysql); } /* Pretty sure statement->ret should be the return value, but it seems to return 1 */ /* using mysql_stmt_errno instead */ if (mysql_stmt_errno (statement->stmt) != 0) { lua_pushnil (L); lua_pushstring (L, mysql_stmt_error (statement->stmt)); values = 2; } if (statement->event > 0) { lua_pushboolean (L, 0); lua_pushnumber (L, convert_mysql_to_ev (statement->event)); values = 2; } if (mysql_stmt_errno (statement->stmt) == 0 || statement->ret == MYSQL_DATA_TRUNCATED) { int d = 1; lua_pushboolean (L, 1); lua_newtable (L); values = 2; for (i = 0; i < column_count; i++) { lua_push_type_t lua_push = mysql_to_lua_push (statement->fields[i].type); const char *name = statement->fields[i].name; if (statement->bind[i].buffer == NULL) { char *buffer = (char *) calloc (real_length[i] + 1, sizeof (char)); statement->bind[i].buffer = buffer; statement->bind[i].buffer_length = real_length[i]; mysql_stmt_fetch_column (statement->stmt, &(statement->bind[i]), i, 0); } if (lua_push == LUA_PUSH_NIL) { if (named_columns) { LUA_PUSH_ATTRIB_NIL (name); } else { LUA_PUSH_ARRAY_NIL (d); } } else if (lua_push == LUA_PUSH_INTEGER) { if (statement->fields[i].type == MYSQL_TYPE_YEAR || statement->fields[i].type == MYSQL_TYPE_SHORT) { if (named_columns) { LUA_PUSH_ATTRIB_INT (name, *(short *) (statement-> bind[i].buffer)); } else { LUA_PUSH_ARRAY_INT (d, *(short *) (statement-> bind[i].buffer)); } } else if (statement->fields[i].type == MYSQL_TYPE_TINY) { if (named_columns) { LUA_PUSH_ATTRIB_INT (name, (int) *(char *) (statement->bind [i].buffer)); } else { LUA_PUSH_ARRAY_INT (d, (int) *(char *) (statement->bind [i].buffer)); } } else { if (named_columns) { LUA_PUSH_ATTRIB_INT (name, *(int *) (statement-> bind[i].buffer)); } else { LUA_PUSH_ARRAY_INT (d, *(int *) (statement-> bind[i].buffer)); } } } else if (lua_push == LUA_PUSH_NUMBER) { if (named_columns) { LUA_PUSH_ATTRIB_FLOAT (name, *(double *) (statement-> bind[i].buffer)); } else { LUA_PUSH_ARRAY_FLOAT (d, *(double *) (statement->bind[i].buffer)); } } else if (lua_push == LUA_PUSH_STRING) { if (statement->fields[i].type == MYSQL_TYPE_TIMESTAMP || statement->fields[i].type == MYSQL_TYPE_DATETIME) { char str[20]; struct st_mysql_time *t = statement->bind[i].buffer; snprintf (str, 20, "%d-%02d-%02d %02d:%02d:%02d", t->year, t->month, t->day, t->hour, t->minute, t->second); if (named_columns) { LUA_PUSH_ATTRIB_STRING (name, str); } else { LUA_PUSH_ARRAY_STRING (d, str); } } else if (statement->fields[i].type == MYSQL_TYPE_TIME) { char str[9]; struct st_mysql_time *t = statement->bind[i].buffer; snprintf (str, 9, "%02d:%02d:%02d", t->hour, t->minute, t->second); if (named_columns) { LUA_PUSH_ATTRIB_STRING (name, str); } else { LUA_PUSH_ARRAY_STRING (d, str); } } else if (statement->fields[i].type == MYSQL_TYPE_DATE) { char str[20]; struct st_mysql_time *t = statement->bind[i].buffer; snprintf (str, 11, "%d-%02d-%02d", t->year, t->month, t->day); if (named_columns) { LUA_PUSH_ATTRIB_STRING (name, str); } else { LUA_PUSH_ARRAY_STRING (d, str); } } else { if (named_columns) { LUA_PUSH_ATTRIB_STRING (name, statement->bind[i].buffer); } else { LUA_PUSH_ARRAY_STRING (d, statement->bind[i].buffer); } } } else if (lua_push == LUA_PUSH_BOOLEAN) { if (named_columns) { LUA_PUSH_ATTRIB_BOOL (name, *(int *) (statement-> bind[i].buffer)); } else { LUA_PUSH_ARRAY_BOOL (d, *(int *) (statement-> bind[i].buffer)); } } else { luaL_error (L, DBI_ERR_UNKNOWN_PUSH); values = 1; } } } else { lua_pushnil (L); values = 1; } } if (statement->bind) { int i; for (i = 0; i < column_count; i++) { free (statement->bind[i].buffer); } free (statement->bind); } return values; } /* }}} */
static int statement_fetch_impl_start (lua_State * L, statement_t * statement) /* {{{ */ { int column_count; unsigned long *real_length = NULL; const char *error_message = NULL; if (!statement->stmt) { luaL_error (L, DBI_ERR_FETCH_INVALID); return 0; } if (!statement->metadata) { luaL_error (L, DBI_ERR_FETCH_NO_EXECUTE); return 0; } column_count = mysql_num_fields (statement->metadata); if (column_count > 0) { int i; real_length = calloc (column_count, sizeof (unsigned long)); statement->bind = malloc (sizeof (MYSQL_BIND) * column_count); memset (statement->bind, 0, sizeof (MYSQL_BIND) * column_count); statement->fields = mysql_fetch_fields (statement->metadata); for (i = 0; i < column_count; i++) { unsigned int length = mysql_buffer_size (&statement->fields[i]); if (length > sizeof (MYSQL_TIME)) { statement->bind[i].buffer = NULL; statement->bind[i].buffer_length = 0; } else { char *buffer = (char *) malloc (length); memset (buffer, 0, length); statement->bind[i].buffer = buffer; statement->bind[i].buffer_length = length; } statement->bind[i].buffer_type = statement->fields[i].type; statement->bind[i].length = &real_length[i]; } if (mysql_stmt_bind_result (statement->stmt, statement->bind)) { error_message = DBI_ERR_BINDING_RESULTS; goto cleanup; } statement->event = mysql_stmt_fetch_start (&(statement->ret), statement->stmt); if (statement->event & MYSQL_WAIT_EXCEPT) { lua_pushnil (L); return 1; } if (statement->event & MYSQL_WAIT_TIMEOUT) { statement->timeout = 1000 * mysql_get_timeout_value_ms (statement->mysql); } lua_pushboolean (L, 1); lua_pushnumber (L, statement->event); return 2; } else { lua_pushnil (L); return 1; } cleanup: free (real_length); if (statement->bind) { int i; for (i = 0; i < column_count; i++) { free (statement->bind[i].buffer); } free (statement->bind); } if (error_message) { luaL_error (L, error_message, mysql_stmt_error (statement->stmt)); return 0; } return 1; } /* }}} */
/* * success,event = statement:execute(...) */ static int statement_execute_start (lua_State * L) /* {{{ */ { int n = lua_gettop (L); statement_t *statement = (statement_t *) luaL_checkudata (L, 1, DBD_MYSQL_STATEMENT); int num_bind_params = n - 1; int expected_params; unsigned char *buffer = NULL; int offset = 0; char *error_message = NULL; char *errstr = NULL; int p; if (statement->metadata) { /* * free existing metadata from any previous executions * TODO mysql_free_result is blocking */ mysql_free_result (statement->metadata); statement->metadata = NULL; } if (!statement->stmt) { lua_pushboolean (L, 0); lua_pushstring (L, DBI_ERR_EXECUTE_INVALID); return 2; } expected_params = mysql_stmt_param_count (statement->stmt); if (expected_params != num_bind_params) { /* * mysql_stmt_bind_param does not handle this condition, * and the client library will segfault if these do no match */ lua_pushboolean (L, 0); lua_pushfstring (L, DBI_ERR_PARAM_MISCOUNT, expected_params, num_bind_params); return 2; } if (num_bind_params > 0) { statement->bind = malloc (sizeof (MYSQL_BIND) * num_bind_params); if (statement->bind == NULL) { luaL_error (L, "Could not alloc bind params\n"); } buffer = (unsigned char *) malloc (num_bind_params * sizeof (double)); memset (statement->bind, 0, sizeof (MYSQL_BIND) * num_bind_params); } for (p = 2; p <= n; p++) { /* {{{ */ int type = lua_type (L, p); int i = p - 2; const char *str = NULL; size_t *str_len = NULL; double *num = NULL; int *boolean = NULL; char err[64]; switch (type) { case LUA_TNIL: statement->bind[i].buffer_type = MYSQL_TYPE_NULL; statement->bind[i].is_null = (my_bool *) 1; break; case LUA_TBOOLEAN: boolean = (int *) (buffer + offset); offset += sizeof (int); *boolean = lua_toboolean (L, p); statement->bind[i].buffer_type = MYSQL_TYPE_LONG; statement->bind[i].is_null = (my_bool *) 0; statement->bind[i].buffer = (char *) boolean; statement->bind[i].length = 0; break; case LUA_TNUMBER: /* * num needs to be it's own * memory here */ num = (double *) (buffer + offset); offset += sizeof (double); *num = lua_tonumber (L, p); statement->bind[i].buffer_type = MYSQL_TYPE_DOUBLE; statement->bind[i].is_null = (my_bool *) 0; statement->bind[i].buffer = (char *) num; statement->bind[i].length = 0; break; case LUA_TSTRING: str_len = (size_t *) (buffer + offset); offset += sizeof (size_t); str = lua_tolstring (L, p, str_len); statement->bind[i].buffer_type = MYSQL_TYPE_STRING; statement->bind[i].is_null = (my_bool *) 0; statement->bind[i].buffer = (char *) str; statement->bind[i].length = str_len; break; default: snprintf (err, sizeof (err) - 1, DBI_ERR_BINDING_TYPE_ERR, lua_typename (L, type)); errstr = err; error_message = DBI_ERR_BINDING_PARAMS; goto cleanup; } } /* }}} */ if (mysql_stmt_bind_param (statement->stmt, statement->bind)) { error_message = DBI_ERR_BINDING_PARAMS; goto cleanup; } statement->event = mysql_stmt_execute_start (&(statement->ret), statement->stmt); if (statement->event & MYSQL_WAIT_EXCEPT) { lua_pushnil (L); lua_pushfstring (L, DBI_ERR_BINDING_EXEC, mysql_stmt_error (statement->stmt)); } if (statement->event & MYSQL_WAIT_TIMEOUT) { statement->timeout = 1000 * mysql_get_timeout_value_ms (statement->mysql); } cleanup: if (statement->bind) { free (statement->bind); } if (buffer) { free (buffer); } if (error_message) { lua_pushboolean (L, 0); lua_pushfstring (L, error_message, errstr ? errstr : mysql_stmt_error (statement->stmt)); return 2; } lua_pushboolean (L, 1); lua_pushnumber (L, convert_mysql_to_ev (statement->event)); return 2; } /* }}} */