/* ** Closes a connection. ** Lua Returns: ** 1 if close was sucsessful, 0 if already closed ** nil and error message otherwise. */ static int conn_close (lua_State *L) { conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_FIREBIRD); luaL_argcheck (L, conn != NULL, 1, "connection expected"); /* already closed */ if(conn->closed != 0) { lua_pushboolean(L, 0); return 1; } /* are all related cursors closed? */ if(conn->lock > 0) return luasql_faildirect(L, "there are still open cursors"); if(conn->autocommit != 0) isc_commit_transaction(conn->env->status_vector, &conn->transaction); else isc_rollback_transaction(conn->env->status_vector, &conn->transaction); if ( CHECK_DB_ERROR(conn->env->status_vector) ) return return_db_error(L, conn->env->status_vector); isc_detach_database(conn->env->status_vector, &conn->db); if ( CHECK_DB_ERROR(conn->env->status_vector) ) return return_db_error(L, conn->env->status_vector); conn->closed = 1; --conn->env->lock; /* check environment can be GC'd */ if(conn->env->lock == 0) lua_unregisterobj(L, conn->env); lua_pushboolean(L, 1); return 1; }
/* ** Connects to a data source. */ static int env_connect(lua_State *L) { const char *sourcename; sqlite3 *conn; const char *errmsg; int res; getenvironment(L); /* validate environment */ sourcename = luaL_checkstring(L, 2); res = sqlite3_open(sourcename, &conn); if (res != SQLITE_OK) { errmsg = sqlite3_errmsg(conn); luasql_faildirect(L, errmsg); sqlite3_close(conn); return 2; } if (lua_isnumber(L, 3)) { sqlite3_busy_timeout(conn, lua_tonumber(L,3)); // TODO: remove this } return create_connection(L, 1, conn); }
/* ** Creates and returns a connection object ** Lua Input: source [, user [, pass]] ** source: data source ** user, pass: data source authentication information ** Lua Returns: ** connection object if successfull ** nil and error message otherwise. */ static int env_connect (lua_State *L) { env_data *env = (env_data *) getenvironment (L); const char *sourcename = luaL_checkstring (L, 2); const char *username = luaL_optstring (L, 3, NULL); const char *password = luaL_optstring (L, 4, NULL); SQLHDBC hdbc; SQLRETURN ret; /* tries to allocate connection handle */ ret = SQLAllocHandle (hDBC, env->henv, &hdbc); if (error(ret)) return luasql_faildirect (L, "connection allocation error."); /* tries to connect handle */ ret = SQLConnect (hdbc, (char *) sourcename, SQL_NTS, (char *) username, SQL_NTS, (char *) password, SQL_NTS); if (error(ret)) { ret = fail(L, hDBC, hdbc); SQLFreeHandle(hDBC, hdbc); return ret; } /* success, return connection object */ return create_connection (L, 1, env, hdbc); }
/* ** Execute an SQL statement. ** Return a Cursor object if the statement is a query, otherwise ** return the number of tuples affected by the statement. */ static int conn_execute(lua_State *L) { conn_data *conn = getconnection(L); const char *statement = luaL_checkstring(L, 2); int res; sqlite3_stmt *vm; const char *errmsg; int numcols; const char *tail; res = sqlite3_prepare(conn->sql_conn, statement, -1, &vm, &tail); if (res != SQLITE_OK) { errmsg = sqlite3_errmsg(conn->sql_conn); return luasql_faildirect(L, errmsg); } /* process first result to retrive query information and type */ res = sqlite3_step(vm); numcols = sqlite3_column_count(vm); /* real query? if empty, must have numcols!=0 */ if ((res == SQLITE_ROW) || ((res == SQLITE_DONE) && numcols)) { sqlite3_reset(vm); return create_cursor(L, 1, conn, vm, numcols); } if (res == SQLITE_DONE) /* and numcols==0, INSERT,UPDATE,DELETE statement */ { sqlite3_finalize(vm); /* return number of columns changed */ lua_pushnumber(L, sqlite3_changes(conn->sql_conn)); return 1; } /* error */ errmsg = sqlite3_errmsg(conn->sql_conn); sqlite3_finalize(vm); return luasql_faildirect(L, errmsg); }
/* ** Finalizes the vm ** Return nil + errmsg or nil in case of sucess */ static int finalize(lua_State *L, cur_data *cur) { const char *errmsg; if (sqlite3_finalize(cur->sql_vm) != SQLITE_OK) { errmsg = sqlite3_errmsg(cur->conn_data->sql_conn); cur->sql_vm = NULL; return luasql_faildirect(L, errmsg); } cur->sql_vm = NULL; lua_pushnil(L); return 1; }
/* ** Creates an Environment and returns it. */ static int create_environment (lua_State *L) { env_data *env; SQLHENV henv; SQLRETURN ret = SQLAllocHandle(hENV, SQL_NULL_HANDLE, &henv); if (error(ret)) return luasql_faildirect(L, "error creating environment."); ret = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if (error(ret)) { ret = luasql_faildirect (L, "error setting SQL version."); SQLFreeHandle (hENV, henv); return ret; } env = (env_data *)lua_newuserdata (L, sizeof (env_data)); luasql_setmeta (L, LUASQL_ENVIRONMENT_ODBC); /* fill in structure */ env->closed = 0; env->conn_counter = 0; env->henv = henv; return 1; }
/* ** Creates an Environment and returns it. */ static int create_environment (lua_State *L) { env_data *env = (env_data *)lua_newuserdata(L, sizeof(env_data)); luasql_setmeta (L, LUASQL_ENVIRONMENT_OCI8); /* fill in structure */ env->closed = 0; env->conn_counter = 0; env->envhp = NULL; env->errhp = NULL; /* maybe OCI_SHARED and OCI_THREADED ??? */ if (OCIEnvCreate ( &(env->envhp), (ub4)OCI_DEFAULT, (dvoid *)0, (dvoid * (*)(dvoid *, size_t)) 0, (dvoid * (*)(dvoid *, dvoid *, size_t)) 0, (void (*)(dvoid *, dvoid *)) 0, (size_t) 0, (dvoid **) 0)) luasql_faildirect (L, "couldn't create environment"); /* error handler */ ASSERT (L, OCIHandleAlloc((dvoid *) env->envhp, (dvoid **) &(env->errhp), /* !!! */ (ub4) OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0), env->errhp); return 1; }
/* ** Closes an environment object ** Lua Returns: ** 1 if close was sucsessful, 0 if already closed ** nil and error message otherwise. */ static int env_close (lua_State *L) { env_data *env = (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_FIREBIRD); luaL_argcheck (L, env != NULL, 1, "environment expected"); /* already closed? */ if(env->closed == 1) { lua_pushboolean(L, 0); return 1; } /* check the lock */ if(env->lock > 0) return luasql_faildirect(L, "there are still open connections"); /* unregister */ lua_unregisterobj(L, env); /* mark as closed */ env->closed = 1; lua_pushboolean(L, 1); return 1; }
/* ** Connects to a data source. ** param: one string for each connection parameter, said ** datasource, username, password, host and port. */ static int env_connect (lua_State *L) { const char *sourcename = luaL_checkstring(L, 2); const char *username = luaL_optstring(L, 3, NULL); const char *password = luaL_optstring(L, 4, NULL); const char *host = luaL_optstring(L, 5, NULL); const int port = luaL_optint(L, 6, 0); MYSQL *conn; getenvironment(L); /* validade environment */ /* Try to init the connection object. */ conn = mysql_init(NULL); if (conn == NULL) return luasql_faildirect(L, LUASQL_PREFIX"Error connecting: Out of memory."); if (!mysql_real_connect(conn, host, username, password, sourcename, port, NULL, 0)) { char error_msg[100]; strncpy (error_msg, mysql_error(conn), 99); mysql_close (conn); /* Close conn if connect failed */ return luasql_failmessage (L, "Error connecting to database. MySQL: ", error_msg); } return create_connection(L, 1, conn); }
/* ** Executes a SQL statement. ** Returns ** cursor object: if there are results or ** row count: number of rows affected by statement if no results */ static int conn_execute (lua_State *L) { conn_data *conn = getconnection(L,1); const char *statement = luaL_checkstring(L, 2); int dialect = (int)luaL_optnumber(L, 3, 3); XSQLVAR *var; long dtype; int i, n, count, stmt_type; cur_data cur; cur.closed = 0; cur.env = conn->env; cur.conn = conn; cur.stmt = NULL; cur.out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(CURSOR_PREALLOC)); cur.out_sqlda->version = SQLDA_VERSION1; cur.out_sqlda->sqln = CURSOR_PREALLOC; /* create a statement to handle the query */ isc_dsql_allocate_statement(conn->env->status_vector, &conn->db, &cur.stmt); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free(cur.out_sqlda); return return_db_error(L, conn->env->status_vector); } /* process the SQL ready to run the query */ isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &cur.stmt, 0, (char*)statement, dialect, cur.out_sqlda); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free(cur.out_sqlda); return return_db_error(L, conn->env->status_vector); } /* what type of SQL statement is it? */ stmt_type = get_statement_type(&cur); if(stmt_type < 0) { free(cur.out_sqlda); return return_db_error(L, conn->env->status_vector); } /* an unsupported SQL statement (something like COMMIT) */ if(stmt_type > 5) { free(cur.out_sqlda); return luasql_faildirect(L, "unsupported SQL statement"); } /* resize the result set if needed */ if (cur.out_sqlda->sqld > cur.out_sqlda->sqln) { n = cur.out_sqlda->sqld; free(cur.out_sqlda); cur.out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); cur.out_sqlda->sqln = n; cur.out_sqlda->version = SQLDA_VERSION1; isc_dsql_describe(conn->env->status_vector, &cur.stmt, 1, cur.out_sqlda); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free(cur.out_sqlda); return return_db_error(L, conn->env->status_vector); } } /* prep the result set ready to handle the data */ for (i=0, var = cur.out_sqlda->sqlvar; i < cur.out_sqlda->sqld; i++, var++) { dtype = (var->sqltype & ~1); /* drop flag bit for now */ switch(dtype) { case SQL_VARYING: var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2); break; case SQL_TEXT: var->sqldata = (char *)malloc(sizeof(char)*var->sqllen); break; case SQL_SHORT: var->sqldata = (char *)malloc(sizeof(short)); break; case SQL_LONG: var->sqldata = (char *)malloc(sizeof(long)); break; case SQL_INT64: var->sqldata = (char *)malloc(sizeof(ISC_INT64)); break; case SQL_FLOAT: var->sqldata = (char *)malloc(sizeof(float)); break; case SQL_DOUBLE: var->sqldata = (char *)malloc(sizeof(double)); break; case SQL_TYPE_TIME: var->sqldata = (char *)malloc(sizeof(ISC_TIME)); break; case SQL_TYPE_DATE: var->sqldata = (char *)malloc(sizeof(ISC_DATE)); break; case SQL_TIMESTAMP: var->sqldata = (char *)malloc(sizeof(ISC_TIMESTAMP)); break; case SQL_BLOB: var->sqldata = (char *)malloc(sizeof(ISC_QUAD)); break; /* TODO : add extra data type handles here */ } if (var->sqltype & 1) { /* allocate variable to hold NULL status */ var->sqlind = (short *)malloc(sizeof(short)); } else { var->sqlind = NULL; } } /* run the query */ isc_dsql_execute(conn->env->status_vector, &conn->transaction, &cur.stmt, 1, NULL); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free_cur(&cur); return return_db_error(L, conn->env->status_vector); } /* if autocommit is set and it's a non SELECT query, commit change */ if(conn->autocommit != 0 && stmt_type > 1) { isc_commit_retaining(conn->env->status_vector, &conn->transaction); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free_cur(&cur); return return_db_error(L, conn->env->status_vector); } } /* what do we return? a cursor or a count */ if(cur.out_sqlda->sqld > 0) { /* a cursor */ cur_data* user_cur; /* open the cursor ready for fetch cycles */ isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt, "dyn_cursor", (unsigned short)NULL); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free_cur(&cur); return return_db_error(L, conn->env->status_vector); } /* copy the cursor into a new lua userdata object */ user_cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_FIREBIRD); memcpy((void*)user_cur, (void*)&cur, sizeof(cur_data)); /* add cursor to the lock count */ lua_registerobj(L, 1, conn); ++conn->lock; } else { /* a count */ if( (count = count_rows_affected(&cur)) < 0 ) { free(cur.out_sqlda); return return_db_error(L, conn->env->status_vector); } lua_pushnumber(L, count); /* totaly finnished with the cursor */ isc_dsql_free_statement(conn->env->status_vector, &cur.stmt, DSQL_drop); free(cur.out_sqlda); } return 1; }
/* ** Retrieves data from the i_th column in the current row ** Input ** types: index in stack of column types table ** hstmt: statement handle ** i: column number ** Returns: ** 0 if successfull, non-zero otherwise; */ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, SQLUSMALLINT i) { const char *tname; char type; /* get column type from types table */ lua_rawgeti (L, LUA_REGISTRYINDEX, coltypes); lua_rawgeti (L, -1, i); /* typename of the column */ tname = lua_tostring(L, -1); if (!tname) return luasql_faildirect(L, "invalid type in table."); type = tname[1]; lua_pop(L, 2); /* pops type name and coltypes table */ /* deal with data according to type */ switch (type) { /* nUmber */ case 'u': { double num; SQLINTEGER got; SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_DOUBLE, &num, 0, &got); if (error(rc)) return fail(L, hSTMT, hstmt); if (got == SQL_NULL_DATA) lua_pushnil(L); else lua_pushnumber(L, num); return 0; } /* bOol */ case 'o': { char b; SQLINTEGER got; SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_BIT, &b, 0, &got); if (error(rc)) return fail(L, hSTMT, hstmt); if (got == SQL_NULL_DATA) lua_pushnil(L); else lua_pushboolean(L, b); return 0; } /* sTring */ case 't': /* bInary */ case 'i': { SQLSMALLINT stype = (type == 't') ? SQL_C_CHAR : SQL_C_BINARY; SQLINTEGER got; char *buffer; luaL_Buffer b; SQLRETURN rc; luaL_buffinit(L, &b); buffer = luaL_prepbuffer(&b); rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); if (got == SQL_NULL_DATA) { lua_pushnil(L); return 0; } /* concat intermediary chunks */ while (rc == SQL_SUCCESS_WITH_INFO) { if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { got = LUAL_BUFFERSIZE; /* get rid of null termination in string block */ if (stype == SQL_C_CHAR) got--; } luaL_addsize(&b, got); buffer = luaL_prepbuffer(&b); rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); } /* concat last chunk */ if (rc == SQL_SUCCESS) { if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { got = LUAL_BUFFERSIZE; /* get rid of null termination in string block */ if (stype == SQL_C_CHAR) got--; } luaL_addsize(&b, got); } if (rc == SQL_ERROR) return fail(L, hSTMT, hstmt); /* return everything we got */ luaL_pushresult(&b); return 0; } } return 0; }