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 2 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_fix_old (lua_State *L)
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: replace terms such as "old" and "new"; over the years these definitions get outdated and stop making sense (e.g. what if a third way of connecting comes up? becomes newer?). I suggest env_connect_convert_args_to_table instead.

Also, "fix" is a heavy term, because it implies something was "broken", when in this case it was clearly not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense

{
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_fix_old(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
32 changes: 32 additions & 0 deletions src/luasql.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,35 @@ 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;
}

/*
** Pulls an optional number value from the table at idx
*/
LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def) {
Copy link
Member

Choose a reason for hiding this comment

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

this is not used anywhere, please remove

lua_Number res = def;

lua_pushstring(L, name);
lua_gettable(L, idx);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not lua_getfield? Is luasql supposed to be compatible with Lua 5.0?

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 believe so, but not a 100% sure on that.

A lot of issue around support across Lua versions, like the change in require behaviour.

Copy link
Contributor

Choose a reason for hiding this comment

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

@hishamhm can you comment on this, should keplerproject c libraries be Lua 5.0 compatible? (I personally don't think so, but maybe it still is a goal)

Copy link
Member

Choose a reason for hiding this comment

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

The last person who I saw keeping Lua 5.0 compatibility in any library was @tomasguisasola. Since he's maintaining LuaSQL drivers for his uses in production, he'd be the best person to ask. Tomás, do you still need 5.0 compatibility for anything?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi

The last person who I saw keeping Lua 5.0 compatibility in any library
was @tomasguisasola [2]. Since he's maintaining LuaSQL drivers for his
uses in production, he'd be the best person to ask. Tomás, do you
still need 5.0 compatibility for anything?
In fact I didn't needed compatibility. It was just a challenge
to let the code compatible to all Lua 5.X versions at the time I
managed to make the code compatible to Lua 5.2 :-)

Regards,
Tomás Guisasola

On 2016-07-04 16:40, Hisham Muhammad wrote:

In src/luasql.c [1]:

  • lua_gettable(L, idx);
    +
  • res = lua_tostring(L, -1);
  • lua_pop(L, 1);
    +
  • return (res != NULL) ? res : def;
    +}
    +
    +/*
    +** Pulls an optional number value from the table at idx
    +/
    +LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx,
    const char
    name, lua_Number def) {
  • lua_Number res = 0;
    +
  • lua_pushstring(L, name);
  • lua_gettable(L, idx);

The last person who I saw keeping Lua 5.0 compatibility in any library
was @tomasguisasola [2]. Since he's maintaining LuaSQL drivers for his
uses in production, he'd be the best person to ask. Tomás, do you
still need 5.0 compatibility for anything?

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub [3], or mute the
thread [4].

Links:

[1]
#54 (comment)
[2] https://github.com/tomasguisasola
[3]
https://github.com/keplerproject/luasql/pull/54/files/b4afdfb93567f8ffaa268ed63115bb9672a106aa#r69487576
[4]
https://github.com/notifications/unsubscribe/AAIA7Q9s-8A6oIFVU_2kSr4SXNoXH8AYks5qSWGngaJpZM4JEQdV

Copy link
Contributor

Choose a reason for hiding this comment

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

@tomasguisasola so the compatibility with 5.0 can be removed? Challenges are fun, but so is replacing boilerplate lua_pushliteral/lua_pushvalue/lua_settable calls with lua_setfield, etc

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi Peter

@tomasguisasola [2] so the compatibility with 5.0 can be removed?
Yes.

Regards,
Tomás

On 2016-07-05 10:26, Peter Melnichenko wrote:

In src/luasql.c [1]:

  • lua_gettable(L, idx);
    +
  • res = lua_tostring(L, -1);
  • lua_pop(L, 1);
    +
  • return (res != NULL) ? res : def;
    +}
    +
    +/*
    +** Pulls an optional number value from the table at idx
    +/
    +LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx,
    const char
    name, lua_Number def) {
  • lua_Number res = 0;
    +
  • lua_pushstring(L, name);
  • lua_gettable(L, idx);

@tomasguisasola [2] so the compatibility with 5.0 can be removed?
Challenges are fun, but so is replacing boilerplate
lua_pushliteral/lua_pushvalue/lua_settable calls with lua_setfield,
etc

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub [3], or mute the
thread [4].

Links:

[1]
#54 (comment)
[2] https://github.com/tomasguisasola
[3]
https://github.com/keplerproject/luasql/pull/54/files/b4afdfb93567f8ffaa268ed63115bb9672a106aa#r69561897
[4]
https://github.com/notifications/unsubscribe/AAIA7RQ7RhQ7s497Z87XUnyGmAB_Bk7rks5qSluNgaJpZM4JEQdV


if(lua_isnumber(L, -1)) {
res = lua_tonumber(L, -1);
}
lua_pop(L, 1);

return res;
}
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