Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for raw ODBC DSN connections. #54

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 115 additions & 13 deletions src/ls_odbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,6 @@ static int conn_setautocommit (lua_State *L) {
return pass(L);
}


/*
** Create a new Connection object and push it on top of the stack.
*/
Expand All @@ -601,32 +600,135 @@ static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc)
return 1;
}

/*
** Uses a DSN string to connect to a ODBC source dynamically
** Lua Input: {
** dsn = <DSN string>,
** }
** Lua Returns:
** connection object if successfull
** nil and error message otherwise.
*/
static int env_table_connect_DSN (lua_State *L)
{
env_data *env = getenvironment (L);
SQLCHAR *sourcename = (SQLCHAR *)luasql_table_optstring(L, 2, "dsn", NULL);

SQLHDBC hdbc;
SQLCHAR sqlOutBuf[4097];
SQLSMALLINT sqlOutLen;
SQLRETURN ret;

ret = SQLSetEnvAttr (env->henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
if (error(ret)) {
return luasql_faildirect (L, "error setting SQL version.");
}

/* 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 = SQLDriverConnect (hdbc, NULL, sourcename, SQL_NTS, sqlOutBuf, 4096,
&sqlOutLen, SQL_DRIVER_NOPROMPT);
if (error(ret)) {
ret = fail(L, hDBC, hdbc);
SQLFreeHandle(hDBC, hdbc);
return ret;
}

/* success, return connection object */
ret = create_connection (L, 1, env, hdbc);
if(ret == 1) {
/* Add the sqlOutBuf string to the results, for diagnostics */
lua_pushlstring(L, (char *)sqlOutBuf, sqlOutLen);
return 2;
}

return ret;
}

/*
** Creates and returns a connection object
** Lua Input: source [, user [, pass]]
** Reforms old connection style to new one
** Lua Input: source, [user, [pass]]
** source: data source
** user, pass: data source authentication information
** Lua Returns:
** new connection details table
*/
static void env_connect_convert_args_to_table (lua_State *L)
{
static const char *const opt_names[] = {
"source",
"user",
"password",
NULL
};
int i, t = lua_gettop(L)-1;

lua_newtable(L);
lua_insert(L, 2);

for(i=0; opt_names[i] != NULL && i<t; ++i) {
lua_pushstring(L, opt_names[i]);
lua_pushvalue(L, i+3);
lua_settable(L, 2);
}

lua_settop(L, 2);
}

/*
** Creates and returns a connection object
** Lua Input: {
** source = <database source/path>,
** user = <user name>,
** password = <user password>,
** or
** dsn = <DSN string>,
** }
** 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);
SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, 2);
SQLCHAR *username = (SQLCHAR*)luaL_optstring (L, 3, NULL);
SQLCHAR *password = (SQLCHAR*)luaL_optstring (L, 4, NULL);
SQLHDBC hdbc;
SQLRETURN ret;
static int env_connect (lua_State *L)
{
env_data *env = getenvironment (L);
SQLCHAR *sourcename = NULL;
SQLCHAR *username = NULL;
SQLCHAR *password = NULL;
SQLHDBC hdbc = NULL;
SQLRETURN ret = 0;

if(lua_gettop(L) < 2) {
return luasql_faildirect(L, "No connection details provided");
}

if(!lua_istable(L, 2)) {
env_connect_convert_args_to_table(L);
}

/* check for the custom DSN connection string */
if(luasql_table_optstring(L, 2, "dsn", NULL) != NULL) {
return env_table_connect_DSN(L);
}

/* get the standard connection details */
sourcename = (SQLCHAR *)luasql_table_optstring(L, 2, "source", NULL);
username = (SQLCHAR *)luasql_table_optstring(L, 2, "user", NULL);
password = (SQLCHAR *)luasql_table_optstring(L, 2, "password", NULL);
Copy link
Member

@hishamhm hishamhm Jul 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just avoid the extra function altogether (and creating a table just to extract its values later), doing this:

    if(lua_istable(L, 2)) {
        /* check for the custom DSN connection string */
        if(luasql_table_optstring(L, 2, "dsn", NULL) != NULL) {
            return env_table_connect_DSN(L);    
        }
        /* get the standard connection details */
        sourcename = (SQLCHAR *)luasql_table_optstring(L, 2, "source", NULL);
        if (!sourcename) {
            return luasql_faildirect (L, "no source given.");
        }
        username = (SQLCHAR *)luasql_table_optstring(L, 2, "user", NULL);
        password = (SQLCHAR *)luasql_table_optstring(L, 2, "password", NULL);
    } else {
        sourcename = (SQLCHAR*)luaL_checkstring (L, 2);
        username = (SQLCHAR*)luaL_optstring (L, 3, NULL);
        password = (SQLCHAR*)luaL_optstring (L, 4, NULL);
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it provides an easy pattern for conversion of the other drivers to the more flexible named parameter style (which this patch shows a good case for)

There's likely going to be additional params added here at some point (e.g. #36) and giving a clear, single place the values are picked up (along with a clear place for backward compatibility) seems a logical arrangement. The function inlines and the overhead of a table is nothing compared to the actual job of connecting to a DB.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, note that the first argument used luaL_checkstring and in your code it just defaults to NULL. I don't know if this breaks the code that follows below, but I supposed checkstring was used there for a reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code still works fine (as in the connection fails as expected)


/* tries to allocate connection handle */
ret = SQLAllocHandle (hDBC, env->henv, &hdbc);
if (error(ret))
if (error(ret)) {
return luasql_faildirect (L, "connection allocation error.");
}

/* tries to connect handle */
ret = SQLConnect (hdbc, sourcename, SQL_NTS,
username, SQL_NTS, password, SQL_NTS);
ret = SQLConnect (hdbc, sourcename, SQL_NTS, username, SQL_NTS, password,
SQL_NTS);
if (error(ret)) {
ret = fail(L, hDBC, hdbc);
SQLFreeHandle(hDBC, hdbc);
Expand Down
15 changes: 15 additions & 0 deletions src/luasql.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,18 @@ LUASQL_API void luasql_set_info (lua_State *L) {
lua_pushliteral (L, "LuaSQL 2.3.0");
lua_settable (L, -3);
}

/*
** Pulls an optional string value from the table at idx
*/
LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def) {
const char* res = NULL;

lua_pushstring(L, name);
lua_gettable(L, idx);

res = lua_tostring(L, -1);
lua_pop(L, 1);

return (res != NULL) ? res : def;
}
3 changes: 3 additions & 0 deletions src/luasql.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ LUASQL_API int luasql_createmeta (lua_State *L, const char *name, const luaL_Reg
LUASQL_API void luasql_setmeta (lua_State *L, const char *name);
LUASQL_API void luasql_set_info (lua_State *L);

LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def);
LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def);

#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501
void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
#endif
Expand Down