Quantcast

Change to gb.db.odbc to use ODBC Connection Strings.

classic Classic list List threaded Threaded
36 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
Hi guys, first devel post.

For some time now I needed to be able to use connection strings (à-la MS) in the ODBC component for connecting to DB Servers. In ODBC parlance, I need to use DSN-less connections.

When this all started, I used only packaged Gambas binaries, and knew just a bit of C and nothing about Linux. I still do, but I gained some self-trust in C and also started tinkering with C unixODBC examples in the 'Net. I even made a Gambas3 class to connect using EXTERN calls to libodbc to see if connection with ConnStrings is possible: It is!

There are two main issues with this approach:
1- My class cannot create and manipulate G3 database objects at low level.
2- Given problem 1 above, any existing DB-access program should be remade since there's no back-compatibility due to not being able to use native G3 DB components.

All this made me look at the gb.db.odbc C sources. I downloaded them and added an IF and a Function.

My modified component source calls my new internal function (static bool is_host_a_connstring(char *host_or_cs)) in order to see if the .Host property of the Connection object is a Host name (no embedded ";" or "="), or a Connection String (has embedded ";" and/or "="). Based on the outcome of the function, it calls SQLConnect (when a Host) as before, or SQLDriverConnect (when a ConnString). Those are the only changes, but more changes MAY be necessary, I do not really know. That was the only place where the component seems to use an ODBC connect call.

My additions/changes were tested successfully in a C example that used SQLConnect which I turned interactive and can now connect to a DSN or using a DSN-less, ConnectionString-based approach.

Now that I have the "new" gb.db.odbc component sources, I need three things:
1- How to compile -if possible- just my ODBC component modified.
2- Once compiled, where to install it so G3 can use it, having backed up the older one just in case. I have both, 32 and 64 bit Ubuntu 14.04 systems available for testing.
3- If my tests after compiling and using go OK, whom/where should I send the sources for further test and inclusion in the main G3 sources?

TIA,
zxMarce.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
Le 19/08/2015 20:20, zxmarce a écrit :

> Hi guys, first devel post.
>
> For some time now I needed to be able to use connection strings (à-la MS) in
> the *ODBC* component for connecting to DB Servers. In ODBC parlance, I need
> to use *DSN-less* connections.
>
> When this all started, I used only packaged Gambas binaries, and knew just a
> bit of C and nothing about Linux. I still do, but I gained some self-trust
> in C and also started tinkering with C unixODBC examples in the 'Net. I even
> made a Gambas3 class to connect using EXTERN calls to *libodbc* to see if
> connection with ConnStrings is possible: It is!
>
> There are two main issues with this approach:
> 1- My class cannot create and manipulate G3 database objects at low level.
> 2- Given problem 1 above, any existing DB-access program should be remade
> since there's no back-compatibility due to not being able to use native G3
> DB components.
>
> All this made me look at the *gb.db.odbc* C sources. I downloaded them and
> added an IF and a Function.
>
> My modified component source calls my new internal function (*static bool
> is_host_a_connstring(char *host_or_cs)*) in order to see if the *.Host
> *property of the *Connection* object is a Host name (no embedded "*;*" or
> "*=*"), or a Connection String (has embedded "*;*" and/or "*=*"). Based on
> the outcome of the function, it calls *SQLConnect* (when a Host) as before,
> or *SQLDriverConnect* (when a ConnString). Those are the only changes, but
> more changes MAY be necessary, I do not really know. That was the only place
> where the component seems to use an ODBC connect call.
>
> My additions/changes were tested successfully in a C example that used
> SQLConnect which I turned interactive and can now connect to a DSN or using
> a DSN-less, ConnectionString-based approach.
>
> Now that I have the "new" *gb.db.odbc* component sources, I need three
> things:
> 1- How to compile -if possible- just my ODBC component modified.
> 2- Once compiled, where to install it so G3 can use it, having backed up the
> older one just in case. I have both, 32 and 64 bit Ubuntu 14.04 systems
> available for testing.
> 3- If my tests after compiling and using go OK, whom/where should I send the
> sources for further test and inclusion in the main G3 sources?
>
> TIA,
> zxMarce.
>

Hi,

Sorry for the delayed answer, the new Gambas bugtracker was a bit urgent...

For a small change, just send a patch to the dev. mailing-list, or the
modified file. The driver is just one source file, so I can check &
merge the change for you.

Otherwise, generally, you must:
- Use subversion.
- Create a sourceforge account.
- Ask me for a write access to the repository.
- Checkout a local copy of the Gambas source tree.
- Remove all other installation of Gambas from your system.
- Do your changes in it, and do all the configuration / compilation /
installation stuff in it. You are using the "development version" of
Gambas now.
- Once everything works, you commit your changes. Everybody else can
access them, and it will be inserted in the next major release of Gambas.

Regards,

--
Benoît Minisini

------------------------------------------------------------------------------
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
In reply to this post by zxMarce
Benoît Minisini wrote
Hi,

Sorry for the delayed answer, the new Gambas bugtracker was a bit urgent...

For a small change, just send a patch to the dev. mailing-list, or the modified file. The driver is just one source file, so I can check & merge the change for you.

Otherwise, generally, you must:
- Use subversion.
- Create a sourceforge account.
- Ask me for a write access to the repository.
- Checkout a local copy of the Gambas source tree.
- Remove all other installation of Gambas from your system.
- Do your changes in it, and do all the configuration / compilation / installation stuff in it. You are using the "development version" of Gambas now.
- Once everything works, you commit your changes. Everybody else can access them, and it will be inserted in the next major release of Gambas.

Regards,

--
Benoît Minisini
Benoît,

The changes for ODBC are kinda small, but even so I think I will go with the whole SVN thingie. That way I won't bother you with further additions or corrections in case it does not work properly, or trashes the whole Gambas-DB ecosystem... Call me paranoid, but I don't like to send a component modification into the wild without testing it. It works in paper and in C, but I could not yet test it as a proper Gambas component.

Just registered on SF, also as zxMarce. Do not grant me write access yet. I'll do the mod checking out the current devel SVN branch (3.8.90) and if my -limited- tests work, I'll ask you for write access.

I'll plan to do my changes on a local copy on a local virtual machine, so I have both "user" and "devel" environments apart (two 32bit virtual machines).
One question regarding this: My host is a 64bit Ubuntu 14.04. If I develop on 32bit, will the module run in the 64bit host? (I presume it will not, but not sure).

I expect to have the virtuals up in a coupla days time.

Thanks again,
zxMarce.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
Benoît,

It's alive! Mbwehehehe...
Well, kind of. It does work as intended, but the connection's .User property must be set to something, even if it is not the real username from the connection string. Otherwise, if the .User property is not set: Bam! SegFault.

This connects OK (using 'Johnny' as user name in the conn string, but not in the conn.User property):
  Dim conn As New Connection
  conn.Type = "odbc"
  conn.Host = "(some connection string);UID=Johnny;(more connection string)"
  conn.User = "!"
  conn.Open
This explodes with SegFault (also using 'Johnny' as user name in the conn string):
  Dim conn As New Connection
  conn.Type = "odbc"
  conn.Host = "(some connection string);UID=Johnny;(more connection string)"
  conn.Open
So, here's where my C drowns. I do not know the "chain of command" (I mean, what is called by who), and thus I have no way of knowing what code needs the .User property populated.
I saw several debug blocks like this in the code but I don't know how to properly activate them, or where to look for their output once they're active:
  #ifdef ODBC_DEBUG_HEADER
  fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__);
  fprintf(stderr,"\topen_database\n");
  fflush(stderr);
  #endif
Care to throw me a 'pointer' to where I should start?
I need to know what happens at low level when Gambas runs Connection.Open; I mean the whole code and file paths that end in gb.db.odbc/src/main.c, so I can check every step.
Also, knowing this, maybe I can make some progress regarding the RowCount = -1 that MSSQL usually returns when running a query that returns a Result.

zxMarce.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Tobias Boege-2
On Thu, 27 Aug 2015, zxMarce wrote:

> Beno??t,
>
> It's alive! Mbwehehehe...
> Well, kind of. It does work as intended, but the connection's *.User*
> property must be set to something, even if it is not the real username from
> the connection string. Otherwise, if the *.User* property is not set: Bam!
> *SegFault*.
>
> This connects OK (using 'Johnny' as user name in the conn string, but not in
> the conn.User property):
>
> This explodes with SegFault (also using 'Johnny' as user name in the conn
> string):
>
> So, here's where my C drowns. I do not know the "chain of command" (I mean,
> what is called by who), and thus I have no way of knowing what code needs
> the *.User* property populated.
> I saw several debug blocks like this in the code but I don't know how to
> properly activate them, or where to look for their output once they're
> active:
>
> Care to throw me a 'pointer' to where I should start?
> I need to know what happens at low level when Gambas runs Connection.Open; I
> mean the whole code and file paths that end in *gb.db.odbc/src/main.c*, so I
> can check every step.
> Also, knowing this, maybe I can make some progress regarding the *RowCount =
> -1* that MSSQL usually returns when running a query that returns a Result.
>

Have a look at main/lib/db. This is the gb.db component which declares the
Connection class. As I don't know how far you've come with programming
components in Gambas, I'll point you to things in small steps.

If you are interested in Connection, open CConnection.c and look for the
declaration of the class interface which you will usually find at the end of
the source file:

  GB_DESC CConnectionDesc[] =
  {
  GB_DECLARE("_Connection", sizeof(CCONNECTION)),
        ...
  };

Notice that the class being declared is actually named "_Connection". But
this is the base class for gb.db's Connection (the real Connection class
is written in the Gambas part of the component and inherits _Connection).

You can find the implementation of Connection.Open by parsing this interface
description: it is the function CCONNECTION_open(). From there you trace
your way to open_connection() where the important line is:

  if (DB_Open(&THIS->desc, &THIS->driver, &THIS->db))
  return;

DB_Open() brings you to main.c where you learn that different database types
are implemented by driver structures. The gb.db.mysql component declares a
driver structure for converting access to the Connection object into MySQL
language, the gb.db.sqlite3 does the same for SQLite3, etc..

Similarly gb.db.odbc has a DECLARE_DRIVER statement somewhere in its main.c.
There it lists ODBC-specific implementations of various methods of the
Connection class. This should enable you to trace the complete path from
the Connection class to your driver.

I think you should not just call the user "Johnny". If there is no sensible
username in ODBC, return an empty string.

Also, as you may not be aware of it, I have written an article about native
component programming for Gambas. It is here[0] and may be of some use for
you.

Regards,
Tobi

[0] http://www-e.uni-magdeburg.de/tboege/gambas/

--
"There's an old saying: Don't change anything... ever!" -- Mr. Monk

------------------------------------------------------------------------------
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
In reply to this post by zxMarce
Le 27/08/2015 14:54, zxMarce a écrit :

> Benoît,
>
> It's alive! Mbwehehehe...
> Well, kind of. It does work as intended, but the connection's *.User*
> property must be set to something, even if it is not the real username from
> the connection string. Otherwise, if the *.User* property is not set: Bam!
> *SegFault*.
>
> This connects OK (using 'Johnny' as user name in the conn string, but not in
> the conn.User property):
>
> This explodes with SegFault (also using 'Johnny' as user name in the conn
> string):
>
> So, here's where my C drowns. I do not know the "chain of command" (I mean,
> what is called by who), and thus I have no way of knowing what code needs
> the *.User* property populated.
> I saw several debug blocks like this in the code but I don't know how to
> properly activate them, or where to look for their output once they're
> active:
>
> Care to throw me a 'pointer' to where I should start?
> I need to know what happens at low level when Gambas runs Connection.Open; I
> mean the whole code and file paths that end in *gb.db.odbc/src/main.c*, so I
> can check every step.
> Also, knowing this, maybe I can make some progress regarding the *RowCount =
> -1* that MSSQL usually returns when running a query that returns a Result.
>
> zxMarce.
>

Note that I didn't write the ODBC driver, so I don't know well what the
SQLxxxx ODBC functions really do.

I noticed that the connection user (desc->user) is directly sent to the
SQLConnect function. Maybe that function does not like getting a NULL
pointer there and something must be done in that case.

I suggest that you send your code: it will be easier to help you.

Regards,

--
Benoît Minisini

------------------------------------------------------------------------------
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
Benoît,

No problem, I know you're not the author of the ODBC component. Precisely, one of my modifications was adding an IF to call either SQLConnect (normal method so far) or SQLDriverConnect; this is the new call, and does not use .User, .Password nor .Name, as they can all be set in the connection string. I added, then, an IF and a FUNCTION.

This is the IF, in main.c's open_database:
if (is_host_a_connstring(desc->host) == TRUE)
{
	/* zxMarce: Connect to Database (desc->host is an ODBC Connection String) */
	retcode = SQLDriverConnect(odbc->odbcHandle, 0, (SQLCHAR *) desc->host, SQL_NTS, 0, 0, 0, SQL_DRIVER_NOPROMPT);
	/* Example - ODBC-ConnString, all one line (must assign this to the Connection.Host property in 
	   Gambas code and then call Connection.Open):
                    "Driver=<driverSectionNameInODBCInst.Ini>;
                     TDS_Version=<useNormally'7.2';
                     Server=<serverNameOrIP>;
                     Port=<serverTcpPort>;
                     Database=<defaultDatabase>;
                     UId=<userName>;
                     Pwd=<password>>"
	*/

} else {
	/* Connect to Database (desc->host is an ODBC Data Source Name) */
	retcode = SQLConnect(odbc->odbcHandle, (SQLCHAR *) desc->host, SQL_NTS, (SQLCHAR *) desc->user, SQL_NTS, (SQLCHAR *) desc->password, SQL_NTS);
}
... And this is the function that new IF calls:
/* 
Internal function to check if the .Host property is actually an ODBC connection string.
ODBC ConnStrings have one or more "ParamName=ParamValue" pairs, delimited by semicolons.
The function helps the component know whether to call SQLConnect (when a host/DSN), 
or SQLDriverConnect (when a ConnString).
zxMarce, 20150814
*/
static int is_host_a_connstring(char *host_or_cs)
{

  int length = strlen(host_or_cs);
  bool connstrCharFound = FALSE;
  int counter;
  char curChar;

  for (counter = 0; counter < length; counter++)
  {
    curChar = host_or_cs[counter];
    if ((curChar == '=') | (curChar == ';'))
    {
      connstrCharFound = TRUE;
      break;
    }
  }

  return connstrCharFound;

}
Thanks,
zxMarce.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
In reply to this post by Tobias Boege-2
Tobias,

I followed -and think understood- most of what you've written, and I plan to read the info linked to see if that clears some doubts I still have.

Regarding "Johnny", it was just an example. What I meant is that if I leave a non-null-string as the .User connection property in Gambas code, the component (or gb.db, I don't know) segfaults. On the other hand, if I assign any non-null-string to the .User connection property, this string is ignored (which is OK for the mod I made, as the user name is supplied in the connection string) and the component connects successfully.

It is a bad annoyance to have to preassign properties that are not actually used in a component.

I think you may continue to hear/read (more nonsense) from me, it looks like you know your C and Gambas components!

Thanks,
zxMarce.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
In reply to this post by zxMarce
Le 27/08/2015 19:14, zxMarce a écrit :

> Benoît,
>
> No problem, I know you're not the author of the ODBC component. Precisely,
> one of my modifications was adding an IF to call either *SQLConnect* (normal
> method so far) or *SQLDriverConnect*; this is the new call, and does not use
> .User, .Password nor .Name, as they can all be set in the connection string.
> I added, then, an IF and a FUNCTION.
>
> This is the IF, in main.c's open_database:
>
> ... And this is the function that new IF calls:
>
> Thanks,
> zxMarce.
>

Please post directly to the developer mailing-list. nabble.com makes
your mail unreadable by removing pictures.

Or don't post pictures. Post the code as text!

--
Benoît Minisini

------------------------------------------------------------------------------
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
Le 27/08/2015 20:03, Benoît Minisini a écrit :

> Le 27/08/2015 19:14, zxMarce a écrit :
>> Benoît,
>>
>> No problem, I know you're not the author of the ODBC component.
>> Precisely,
>> one of my modifications was adding an IF to call either *SQLConnect*
>> (normal
>> method so far) or *SQLDriverConnect*; this is the new call, and does
>> not use
>> .User, .Password nor .Name, as they can all be set in the connection
>> string.
>> I added, then, an IF and a FUNCTION.
>>
>> This is the IF, in main.c's open_database:
>>
>> ... And this is the function that new IF calls:
>>
>> Thanks,
>> zxMarce.
>>
>
> Please post directly to the developer mailing-list. nabble.com makes
> your mail unreadable by removing pictures.
>
> Or don't post pictures. Post the code as text!
>

If your function (is_host_a_connstring) returns a boolean value, you can
use 'bool' as return datatype.

If you are testing a boolean value, writing that:

        if (xxx == TRUE)

is not needed. Just write:

        if (xxx)

Regards,

--
Benoît Minisini

------------------------------------------------------------------------------
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
On 2015-08-27 15:06, Benoît Minisini wrote:
Le 27/08/2015 20:03, Benoît Minisini a écrit :
Le 27/08/2015 19:14, zxMarce a écrit :
Benoît,
No problem, I know you're not the author of the ODBC component.
Precisely, one of my modifications was adding an IF to call either SQLConnect (normal method so far) or SQLDriverConnect; this is the new call, and does not use .User, .Password nor .Name, as they can all be set in the connection string.
I added, then, an IF and a FUNCTION. This is the IF, in main.c's open_database:
... And this is the function that new IF calls:
Thanks,
zxMarce.
Please post directly to the developer mailing-list. nabble.com makes your mail unreadable by removing pictures.
Or don't post pictures. Post the code as text!
If your function (is_host_a_connstring) returns a boolean value, you can use 'bool' as return datatype.
If you are testing a boolean value, writing that:
    if (xxx == TRUE)
is not needed. Just write:
    if (xxx)
Regards,
Wow! I did not know Nabble removed text blocks from mails. I now also see the missing segments in my mails. I used 'Raw Text' blocks to post the code.
Anyway, for post completeness, the missing if was as follows:

    if (is_host_a_connstring(desc->host) == TRUE)
    {
        /* zxMarce: Connect to Database (desc->host is an ODBC Connection String) */
        retcode = SQLDriverConnect(odbc->odbcHandle, 0, (SQLCHAR *) desc->host, SQL_NTS, 0, 0, 0, SQL_DRIVER_NOPROMPT);
        /* Example - ODBC-ConnString, all one line (must assign this to the Connection.Host property in
           Gambas code and then call Connection.Open):
                        "Driver=<driverSectionNameInODBCInst.Ini>;
                         TDS_Version=<useNormally'7.2';
                         Server=<serverNameOrIP>;
                         Port=<serverTcpPort>;
                         Database=<defaultDatabase>;
                         UId=<userName>;
                         Pwd=<password>>"
        */
    } else {
        /* Connect to Database (desc->host is an ODBC Data Source Name) */
        retcode = SQLConnect(odbc->odbcHandle, (SQLCHAR *) desc->host, SQL_NTS, (SQLCHAR *) desc->user, SQL_NTS, (SQLCHAR *) desc->password, SQL_NTS);
    }

And the missing helper function was:

    /*
    Internal function to check if the .Host property is actually an ODBC connection string.
    ODBC ConnStrings have one or more "ParamName=ParamValue" pairs, delimited by semicolons.
    The function helps the component know whether to call SQLConnect (when a host/DSN),
    or SQLDriverConnect (when a ConnString).
    zxMarce, 20150814
    */
    static int is_host_a_connstring(char *host_or_cs)
    {
   
      int length = strlen(host_or_cs);
      bool connstrCharFound = FALSE;
      int counter;
      char curChar;
   
      for (counter = 0; counter < length; counter++)
      {
        curChar = host_or_cs[counter];
        if ((curChar == '=') | (curChar == ';'))
        {
          connstrCharFound = TRUE;
          break;
        }
      }
   
      return connstrCharFound;
   
    }

Now I will change the function type from INT to BOOL and reform the IF. Thanks for the tip!

Regards,

------------------------------------------------------------------------------

_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
In reply to this post by Benoît Minisini
Benoît,

Continuing here as you requested.
I found the problem in th driver that causes it to return 0 instead of -1 in the RowCount. The offending line and my patch:

  //20150914 - zxMarce: Do NOT mark the STMT as Scrollable; it makes SQLRowCount always return 0 rows instead of -1.
  //retcode = SQLSetStmtAttr(odbcres->odbcStatHandle, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_SCROLLABLE, 0);
  retcode = SQLSetStmtAttr(odbcres->odbcStatHandle, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_NONSCROLLABLE, 0);

It just needed a constant change from SQL_SCROLLABLE to SQL_NONSCROLLABLE.
Looks like the intention was to make a proper driver, with forward and back scroll, but for some reason it was not completed.

Now the problem propagates to Result.MoveNext and Result.Available. They loop forever, even when there's no more data to fetch.
I made a further change in query_fill (removed a GB.Error that raised 'ODBC_NO_MORE_DATA' and added a return TRUE), but that did not get .MoveNext or .Available fixed:

  if((retcode2 == SQL_NO_DATA_FOUND) || (retcode2==SQL_NO_DATA))
  {
    //GB.Error("ODBC_END_OF_DATA!"); //20150914 - zxMarce: Removed so .MoveNext and .Available work?
    return TRUE; //20150914 - zxMarce: Try to make .MoveNext fail when no more data
  }

I think when we (you?) fix these problems, we will finally have a working Gambas ODBC subsystem.

Thanks,

On 2015-08-27 15:06, Benoît Minisini wrote:
Le 27/08/2015 20:03, Benoît Minisini a écrit :
Le 27/08/2015 19:14, zxMarce a écrit :
Benoît,

No problem, I know you're not the author of the ODBC component.
Precisely, one of my modifications was adding an IF to call either *SQLConnect* (normal method so far) or *SQLDriverConnect*; this is the new call, and does not use .User, .Password nor .Name, as they can all be set in the connection string.
I added, then, an IF and a FUNCTION.
This is the IF, in main.c's open_database:
... And this is the function that new IF calls:
Thanks,
zxMarce.
Please post directly to the developer mailing-list. nabble.com makes your mail unreadable by removing pictures.
Or don't post pictures. Post the code as text!
If your function (is_host_a_connstring) returns a boolean value, you can use 'bool' as return datatype.
If you are testing a boolean value, writing that:
    if (xxx == TRUE)
is not needed. Just write:
    if (xxx)
Regards,
--
zxMarce.

------------------------------------------------------------------------------

_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
Le 14/09/2015 17:41, ML a écrit :

> Benoît,
>
> Continuing here as you requested.
> I found the problem in th driver that causes it to return 0 instead of
> -1 in the RowCount. The offending line and my patch:
>
>    //20150914 - zxMarce: Do NOT mark the STMT as Scrollable; it makes
> SQLRowCount always return 0 rows instead of -1.
>    //retcode = SQLSetStmtAttr(odbcres->odbcStatHandle,
> SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_SCROLLABLE, 0);
>    retcode = SQLSetStmtAttr(odbcres->odbcStatHandle,
> SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_NONSCROLLABLE, 0);
>
> It just needed a constant change from SQL_SCROLLABLE to SQL_NONSCROLLABLE.
> Looks like the intention was to make a proper driver, with forward and
> back scroll, but for some reason it was not completed.
>
> Now the problem propagates to Result.MoveNext and Result.Available. They
> loop forever, even when there's no more data to fetch.
> I made a further change in query_fill (removed a GB.Error that raised
> 'ODBC_NO_MORE_DATA' and added a return TRUE), but that did not get
> .MoveNext or .Available fixed:
>
>    if((retcode2 == SQL_NO_DATA_FOUND) || (retcode2==SQL_NO_DATA))
>    {
>      //GB.Error("ODBC_END_OF_DATA!"); //20150914 - zxMarce: Removed so
> .MoveNext and .Available work?
>      return TRUE; //20150914 - zxMarce: Try to make .MoveNext fail when
> no more data
>    }
>
> I think when we (you?) fix these problems, we will finally have a
> working Gambas ODBC subsystem.
>
> Thanks,
>

gb.db.odbc tells Gambas if he has a function to seek through a record at
line 564 (with the no_seek connection flag).

But having that function does not mean that for a specific ODBC driver,
seeking is actually possible.

In the query_fill() function at line 1152 you will see what the driver does:

- If the seek function exist, then:
   - If the result has been marked scrollable successfully, then move to
the specified position.
   - If the result is not scrollable, then move to the next record.
- Otherwise, if the seek function does not exist, then move to the next
record. If moving to the next record was not requested ('next'
argument), then return TRUE (meaning an error).

So, apparently, it's just Gambas that does not handle the no_seek
connection flag at the moment.

And we should force the query count to be -1 when seeking is not
possible, whatever the reason (no seek function, or unscrollable query
result).

--
Benoît Minisini

------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
On 2015-09-15 21:24, Benoît Minisini wrote:
Le 14/09/2015 17:41, ML a écrit :
Benoît,
Continuing here as you requested.
I found the problem in th driver that causes it to return 0 instead of -1 in the RowCount. The offending line and my patch:
   //20150914 - zxMarce: Do NOT mark the STMT as Scrollable; it makes SQLRowCount always return 0 rows instead of -1.
   //retcode = SQLSetStmtAttr(odbcres->odbcStatHandle, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_SCROLLABLE, 0);
   retcode = SQLSetStmtAttr(odbcres->odbcStatHandle, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_NONSCROLLABLE, 0);
It just needed a constant change from SQL_SCROLLABLE to SQL_NONSCROLLABLE.
Looks like the intention was to make a proper driver, with forward and back scroll, but for some reason it was not completed.
Now the problem propagates to Result.MoveNext and Result.Available. They loop forever, even when there's no more data to fetch.
I made a further change in query_fill (removed a GB.Error that raised 'ODBC_NO_MORE_DATA' and added a return TRUE), but that did not get .MoveNext or .Available fixed:
   if((retcode2 == SQL_NO_DATA_FOUND) || (retcode2==SQL_NO_DATA))
   {
     //GB.Error("ODBC_END_OF_DATA!"); //20150914 - zxMarce: Removed so .MoveNext and .Available work?
     return TRUE; //20150914 - zxMarce: Try to make .MoveNext fail when no more data
   }
I think when we (you?) fix these problems, we will finally have a working Gambas ODBC subsystem.
Thanks,
gb.db.odbc tells Gambas if he has a function to seek through a record at line 564 (with the no_seek connection flag).
But having that function does not mean that for a specific ODBC driver, seeking is actually possible.
In the query_fill() function at line 1152 you will see what the driver does:
- If the seek function exist, then:
   - If the result has been marked scrollable successfully, then move to the specified position.
   - If the result is not scrollable, then move to the next record.
- Otherwise, if the seek function does not exist, then move to the next record. If moving to the next record was not requested ('next' argument), then return TRUE (meaning an error).
So, apparently, it's just Gambas that does not handle the no_seek connection flag at the moment.
And we should force the query count to be -1 when seeking is not possible, whatever the reason (no seek function, or unscrollable query result).

Benoît,

I've been investigating and came to the same conclusion you say regarding how the nested IFs work in query_fill.

Unfortunately, making no_seek TRUE is not the answer (tried it), but setting the ODBC Statement Handle's SQL_ATTR_CURSOR_SCROLLABLE attribute to SQL_NONSCROLLABLE instead of SQL_SCROLLABLE makes the trick of returning -1 as rowcount. I don't like forcing flag values because they may come in handy for other purposes later. On the other hand, the ODBC Statement Handle is created once per Connection.Exec call, so I'm more comfortable with changing that.

The problem that arises now is making Result.MoveNext (and preferably Result.Available also) work as it (they) should.

Changing the SCROLLABLE to NONSCROLLABLE achieves, then, 2 things:
1- The rowcount returned is -1.
2- The Result object has data and is not Null (with a rowcount=0 the Result object raises a "Result is not available" error).

Point 2 is what actually enables fetching data. The problem is now when all data has been retrieved: Result.Available is (still) TRUE and any subsequent Result.MoveNext raises an awful "ODBC_END_OF_DATA" error.
This not only forces the programmer to resort to convoluted techniques to fetch the data, it will also make any data-aware control fail if they rely on .MoveNext and .Available working.

What I did find, though, is that CResult.c is calling THIS->driver->Result.Fill and discarding the return value. Does not look wise, as the driver (at least gb.db.odbc) is actually returning whether the fill was or not successful.
I added another three changes to CResult.c that fixed the problem and made Result.Available work, but Result.MoveNext still raises the error, although trappable by Try res.MoveNext() in Gambas:

  //20150916 zxMarce: Make .Available depend on the result of driver's query_fill
  bool myUnavailable = FALSE;

  //20150916 zxMarce: Make .Available depend on the result of driver's query_fill
  /* THIS->driver->Result.Fill(&THIS->conn->db,
                                           THIS->handle,
                                           pos,
                                           THIS->buffer,
                                           (pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
                                          ); */
  myUnavailable = THIS->driver->Result.Fill(&THIS->conn->db,
                                                                       THIS->handle,
                                                                       pos,
                                                                       THIS->buffer,
                                                                       (pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
                                                                      );

  //20150916 zxMarce: Make .Available depend on the result of driver's query_fill
  //THIS->available = TRUE;
  THIS->available = !myUnavailable;
  return FALSE;

I now can fetch data without resorting to unnecessary error handling, although fixing the error risen by Result.MoveNext and making it return TRUE instead is still pending.
I have to clean up the code a little bit and I think I can send it so you can check it.
I don't know if the "myUnavailable" patch above will break other drivers' functionality, and have no other databases than MSSQL to test.


------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
Le 16/09/2015 16:36, ML a écrit :

> *On 2015-09-15 21:24, Benoît Minisini wrote:*
>> *Le 14/09/2015 17:41, ML a écrit : *
>>> Benoît,
>>> Continuing here as you requested.
>>> I found the problem in th driver that causes it to return 0 instead
>>> of -1 in the RowCount. The offending line and my patch:
>>>    //20150914 - zxMarce: Do NOT mark the STMT as Scrollable; it makes
>>> SQLRowCount always return 0 rows instead of -1.
>>>    //retcode = SQLSetStmtAttr(odbcres->odbcStatHandle,
>>> SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_SCROLLABLE, 0);
>>>    retcode = SQLSetStmtAttr(odbcres->odbcStatHandle,
>>> SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_NONSCROLLABLE, 0);
>>> It just needed a constant change from SQL_SCROLLABLE to
>>> SQL_NONSCROLLABLE.
>>> Looks like the intention was to make a proper driver, with forward
>>> and back scroll, but for some reason it was not completed.
>>> Now the problem propagates to Result.MoveNext and Result.Available.
>>> They loop forever, even when there's no more data to fetch.
>>> I made a further change in query_fill (removed a GB.Error that raised
>>> 'ODBC_NO_MORE_DATA' and added a return TRUE), but that did not get
>>> .MoveNext or .Available fixed:
>>>    if((retcode2 == SQL_NO_DATA_FOUND) || (retcode2==SQL_NO_DATA))
>>>    {
>>>      //GB.Error("ODBC_END_OF_DATA!"); //20150914 - zxMarce: Removed
>>> so .MoveNext and .Available work?
>>>      return TRUE; //20150914 - zxMarce: Try to make .MoveNext fail
>>> when no more data
>>>    }
>>> I think when we (you?) fix these problems, we will finally have a
>>> working Gambas ODBC subsystem.
>>> Thanks,
>> gb.db.odbc tells Gambas if he has a function to seek through a record
>> at line 564 (with the no_seek connection flag).
>> But having that function does not mean that for a specific ODBC
>> driver, seeking is actually possible.
>> In the query_fill() function at line 1152 you will see what the driver
>> does:
>> - If the seek function exist, then:
>>    - If the result has been marked scrollable successfully, then move
>> to the specified position.
>>    - If the result is not scrollable, then move to the next record.
>> - Otherwise, if the seek function does not exist, then move to the
>> next record. If moving to the next record was not requested ('next'
>> argument), then return TRUE (meaning an error).
>> So, apparently, it's just Gambas that does not handle the no_seek
>> connection flag at the moment.
>> And we should force the query count to be -1 when seeking is not
>> possible, whatever the reason (no seek function, or unscrollable query
>> result).
>
> Benoît,
>
> I've been investigating and came to the same conclusion you say
> regarding how the nested IFs work in query_fill.
>
> Unfortunately, making no_seek TRUE is not the answer (tried it), but
> setting the ODBC Statement Handle's SQL_ATTR_CURSOR_SCROLLABLE attribute
> to SQL_NONSCROLLABLE instead of SQL_SCROLLABLE makes the trick of
> returning -1 as rowcount. I don't like forcing flag values because they
> may come in handy for other purposes later. On the other hand, the ODBC
> Statement Handle is created once per Connection.Exec call, so I'm more
> comfortable with changing that.
>
> The problem that arises now is making Result.MoveNext (and preferably
> Result.Available also) work as it (they) should.
>
> Changing the SCROLLABLE to NONSCROLLABLE achieves, then, 2 things:
> 1- The rowcount returned is -1.
> 2- The Result object has data and is not Null (with a rowcount=0 the
> Result object raises a "Result is not available" error).
>
> Point 2 is what actually enables fetching data. The problem is now when
> all data has been retrieved: Result.Available is (still) TRUE and any
> subsequent Result.MoveNext raises an awful "ODBC_END_OF_DATA" error.
> This not only forces the programmer to resort to convoluted techniques
> to fetch the data, it will also make any data-aware control fail if they
> rely on .MoveNext and .Available working.
>
> What I did find, though, is that CResult.c is calling
> THIS->driver->Result.Fill and discarding the return value. Does not look
> wise, as the driver (at least gb.db.odbc) is actually returning whether
> the fill was or not successful.
> I added another three changes to CResult.c that fixed the problem and
> made Result.Available work, but Result.MoveNext still raises the error,
> although trappable by Try res.MoveNext() in Gambas:
>
>    //20150916 zxMarce: Make .Available depend on the result of driver's
> query_fill
>    bool myUnavailable = FALSE;
>
>    //20150916 zxMarce: Make .Available depend on the result of driver's
> query_fill
>    /* THIS->driver->Result.Fill(&THIS->conn->db,
>                                             THIS->handle,
>                                             pos,
>                                             THIS->buffer,
>                                             (pos > 0) && (pos ==
> (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
>                                            ); */
>    myUnavailable = THIS->driver->Result.Fill(&THIS->conn->db,
> THIS->handle,
>                                                             pos,
> THIS->buffer,
>                                                             (pos > 0) &&
> (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
>                                                            );
>
>    //20150916 zxMarce: Make .Available depend on the result of driver's
> query_fill
>    //THIS->available = TRUE;
>    THIS->available = !myUnavailable;
>    return FALSE;
>
> I now can fetch data without resorting to unnecessary error handling,
> although fixing the error risen by Result.MoveNext and making it return
> TRUE instead is still pending.
> I have to clean up the code a little bit and I think I can send it so
> you can check it.
> I don't know if the "myUnavailable" patch above will break other
> drivers' functionality, and have no other databases than MSSQL to test.
>

If the ODBC driver can "scroll" its query result, we must use that. Why
forcing the NONSCROLLABLE flag?

As for the rest, I must first modify the gb.db component to add support
for "move forward only" Result object, otherwise your patches are just
hacks. I will tell you...

Regards,

--
Benoît Minisini

------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
On 2015-09-16 12:19, Benoît Minisini wrote:

> Le 16/09/2015 16:36, ML a écrit :
>> On 2015-09-15 21:24, Benoît Minisini wrote:
>>> Le 14/09/2015 17:41, ML a écrit :
>>>> Benoît,
>>>> Continuing here as you requested.
>>>> I found the problem in th driver that causes it to return 0 instead
>>>> of -1 in the RowCount. The offending line and my patch:
>>>>    //20150914 - zxMarce: Do NOT mark the STMT as Scrollable; it
>>>> makes SQLRowCount always return 0 rows instead of -1.
>>>>    //retcode = SQLSetStmtAttr(odbcres->odbcStatHandle,
>>>> SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_SCROLLABLE, 0);
>>>>    retcode = SQLSetStmtAttr(odbcres->odbcStatHandle,
>>>> SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_NONSCROLLABLE, 0);
>>>> It just needed a constant change from SQL_SCROLLABLE to
>>>> SQL_NONSCROLLABLE.
>>>> Looks like the intention was to make a proper driver, with forward
>>>> and back scroll, but for some reason it was not completed.
>>>> Now the problem propagates to Result.MoveNext and Result.Available.
>>>> They loop forever, even when there's no more data to fetch.
>>>> I made a further change in query_fill (removed a GB.Error that
>>>> raised 'ODBC_NO_MORE_DATA' and added a return TRUE), but that did
>>>> not get .MoveNext or .Available fixed:
>>>>    if((retcode2 == SQL_NO_DATA_FOUND) || (retcode2==SQL_NO_DATA))
>>>>    {
>>>>      //GB.Error("ODBC_END_OF_DATA!"); //20150914 - zxMarce: Removed
>>>> so .MoveNext and .Available work?
>>>>      return TRUE; //20150914 - zxMarce: Try to make .MoveNext fail
>>>> when no more data
>>>>    }
>>>> I think when we (you?) fix these problems, we will finally have a
>>>> working Gambas ODBC subsystem.
>>>> Thanks,
>>>>
>>> gb.db.odbc tells Gambas if he has a function to seek through a
>>> record at line 564 (with the no_seek connection flag).
>>> But having that function does not mean that for a specific ODBC
>>> driver, seeking is actually possible.
>>> In the query_fill() function at line 1152 you will see what the
>>> driver does:
>>> - If the seek function exist, then:
>>>    - If the result has been marked scrollable successfully, then
>>> move to the specified position.
>>>    - If the result is not scrollable, then move to the next record.
>>> - Otherwise, if the seek function does not exist, then move to the
>>> next record. If moving to the next record was not requested ('next'
>>> argument), then return TRUE (meaning an error).
>>> So, apparently, it's just Gambas that does not handle the no_seek
>>> connection flag at the moment.
>>> And we should force the query count to be -1 when seeking is not
>>> possible, whatever the reason (no seek function, or unscrollable
>>> query result).
>> Benoît,
>> I've been investigating and came to the same conclusion you say
>> regarding how the nested IFs work in query_fill.
>> Unfortunately, making no_seek TRUE is not the answer (tried it), but
>> setting the ODBC Statement Handle's SQL_ATTR_CURSOR_SCROLLABLE
>> attribute to SQL_NONSCROLLABLE instead of SQL_SCROLLABLE makes the
>> trick of returning -1 as rowcount. I don't like forcing flag values
>> because they may come in handy for other purposes later. On the other
>> hand, the ODBC Statement Handle is created once per Connection.Exec
>> call, so I'm more comfortable with changing that.
>> The problem that arises now is making Result.MoveNext (and preferably
>> Result.Available also) work as it (they) should.
>> Changing the SCROLLABLE to NONSCROLLABLE achieves, then, 2 things:
>> 1- The rowcount returned is -1.
>> 2- The Result object has data and is not Null (with a rowcount=0 the
>> Result object raises a "Result is not available" error).
>> Point 2 is what actually enables fetching data. The problem is now
>> when all data has been retrieved: Result.Available is (still) TRUE
>> and any subsequent Result.MoveNext raises an awful "ODBC_END_OF_DATA"
>> error.
>> This not only forces the programmer to resort to convoluted
>> techniques to fetch the data, it will also make any data-aware
>> control fail if they rely on .MoveNext and .Available working.
>> What I did find, though, is that CResult.c is calling
>> THIS->driver->Result.Fill and discarding the return value. Does not
>> look wise, as the driver (at least gb.db.odbc) is actually returning
>> whether the fill was or not successful.
>> I added another three changes to CResult.c that fixed the problem and
>> made Result.Available work, but Result.MoveNext still raises the
>> error, although trappable by Try res.MoveNext() in Gambas:
>>    //20150916 zxMarce: Make .Available depend on the result of
>> driver's query_fill
>>    bool myUnavailable = FALSE;
>>    //20150916 zxMarce: Make .Available depend on the result of
>> driver's query_fill
>>    /* THIS->driver->Result.Fill(&THIS->conn->db,
>>                                             THIS->handle,
>>                                             pos,
>>                                             THIS->buffer,
>>                                             (pos > 0) && (pos ==
>> (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
>>                                            ); */
>>    myUnavailable = THIS->driver->Result.Fill(&THIS->conn->db,
>> THIS->handle,
>>                                                            pos,
>> THIS->buffer,
>>                                                           (pos > 0)
>> && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
>> );
>>    //20150916 zxMarce: Make .Available depend on the result of
>> driver's query_fill
>>    //THIS->available = TRUE;
>>    THIS->available = !myUnavailable;
>>    return FALSE;
>>
>> I now can fetch data without resorting to unnecessary error handling,
>> although fixing the error risen by Result.MoveNext and making it
>> return TRUE instead is still pending.
>> I have to clean up the code a little bit and I think I can send it so
>> you can check it. I don't know if the "myUnavailable" patch above
>> will break other drivers' functionality, and have no other databases
>> than MSSQL to test.
>
> If the ODBC driver can "scroll" its query result, we must use that.
> Why forcing the NONSCROLLABLE flag?
> As for the rest, I must first modify the gb.db component to add
> support for "move forward only" Result object, otherwise your patches
> are just hacks. I will tell you...
> Regards,
Benoît,

Ok, I think I misunderstood your meaning (and maybe I still do!). If I
can help in any way, just say the word.
I just forced the attribute because it looked to me that at least MSSQL
does not work with unixODBC correctly and I wanted to make the driver as
general as possible.

But, rereading your post, you say that it may be Gambas misbehaving, so
I'll wait for you.
In the meantime, I'll use what I already have because it works and I
need it. I will not publish my changes other than here as I already did.

Thanks,
zxMarce.

------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
Le 16/09/2015 20:40, ML a écrit :

>
> Ok, I think I misunderstood your meaning (and maybe I still do!). If I
> can help in any way, just say the word.
> I just forced the attribute because it looked to me that at least MSSQL
> does not work with unixODBC correctly and I wanted to make the driver as
> general as possible.
>
> But, rereading your post, you say that it may be Gambas misbehaving, so
> I'll wait for you.
> In the meantime, I'll use what I already have because it works and I
> need it. I will not publish my changes other than here as I already did.
>
> Thanks,
> zxMarce.
>

Can you start playing with revision #7316?

I have modified gb.db and gb.db.odbc so that the query_fill() method return:

- DB_ERROR constant if query_fill() fails.
- DB_OK constant if it succeeds.
- DB_NO_DATA constant if we are moving forward and there is no row anymore.

The query_init() must return -1 in its 'count' argument to indicate that
the result is "forward only".

Then 'gb.db' will raise an error if the user tries to use the Resule
objet for something else that just forwarding one row by one row.

Tell me what you can do with that.

Regards,

--
Benoît Minisini

------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
This post was updated on .
Benoît,

First off, with ODBC all .MoveXXX methods fail with "Result is forward only" as expected, except .MoveFirst() and .MoveNext(). That is OK.

Second, I got the low level driver to return -1 as record count by specifying different "TDS_Version" values in the connection string. For example, "TDS_Version=7.2" returns zero, other versions return the expected -1 (4.2, 7, 7.1, 7.3, etc). This is also OK, because it is user-controllable.

But now that I have tested your mods, I must report the bad news: Both .MoveNext and .Available properties always return FALSE and TRUE respectively, so there is no way to cleanly exit a data retrieval loop.

I think the problem is in CResult.c at line 145. That particular line calls the driver's query_fill function but disregards the return value, so the DB_ERROR, DB_OK and DB_NO_DATA return values are ignored.

If it helps, I cooked this mod in CResult.c, which made it work as expected with ODBC, adjusting the return values of .MoveNext and .Available:

  //Next original line (CResult.c:145) does not store the FILL call
  //result, therefore all calls will be assumed OK even when at least
  //the ODBC driver says it failed.
  /*THIS->driver->Result.Fill(&THIS->conn->db,
                              THIS->handle,
                              pos,
                              THIS->buffer,
                              (pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
                             );*/

  //This next replacement line stores the FILL call result
  //in fillFailed so it can be checked later.
  int fillFailed = THIS->driver->Result.Fill(&THIS->conn->db,
                                         THIS->handle,
                                         pos,
                                         THIS->buffer,
                                         (pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
                                        );

  //Next, check whether the FILL above succeeded or failed and
  //return proper states in Result.MoveNext and Result.Available.
  if (fillFailed)
  {
    THIS->available = FALSE;
    return TRUE;
  }

Finally, if I misinterpreted your intentions (yet again), please disregard my suggestions.

Regards,
zxMarce.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

Benoît Minisini
Is it better with revision #7334?

Le 21/09/2015 15:08, zxMarce a écrit :

> Benoît,
>
> First off, with ODBC all .MoveXXX methods fail with "Result is forward only"
> as expected, except .MoveFirst() and .MoveNext(). That is OK.
>
> Second, I got the low level driver to return -1 as record count by
> specifying different "TDS_Version" values in the connection string. For
> example, "TDS_Version=7.2" returns zero, other versions return the expected
> -1 (4.2, 7, 7.1, 7.3, etc). This is also OK, because it is
> user-controllable.
>
> But now that I have tested your mods, I must report the bad news: Both
> .MoveNext and .Available properties always return FALSE, so there is no way
> to cleanly exit a data retrieval loop.
>
> I think the problem is in CResult.c at around line 145. That particular line
> calls the driver's query_fill function but disregards the return value, so
> the DB_ERROR, DB_OK and DB_NO_DATA return values are ignored.
>
> If it helps, I cooked this mod in CResult.c, which made it work as expected
> with ODBC, adjusting the return values of .MoveNext and .Available:
>
>    //Next original line does not store the FILL call result,
>    //therefore any call will be assumed OK even when the
>    //ODBC driver says it failed.
>    /*THIS->driver->Result.Fill(&THIS->conn->db,
>                                THIS->handle,
>                                pos,
>                                THIS->buffer,
>                                (pos > 0) && (pos ==
> (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
>                               );*/
>
>    //This next replacement line stores the FILL call result
>    //in fillFailed so it can be checked later.
>    int fillFailed = THIS->driver->Result.Fill(&THIS->conn->db,
>                                           THIS->handle,
>                                           pos,
>                                           THIS->buffer,
>                                           (pos > 0) && (pos ==
> (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
>                                          );
>
>    //Next, check whether the FILL above succeeded or failed and
>    //return proper states in Result.MoveNext and Result.Available.
>    if (fillFailed)
>    {
>      THIS->available = FALSE;
>      return TRUE;
>    }
>
> Finally, if I misinterpreted your intentions (yet again), please disregard
> my suggestions.
>
> Regards,
> zxMarce.
>


--
Benoît Minisini

------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Change to gb.db.odbc to use ODBC Connection Strings.

zxMarce
Benoît,

Revision 7334 did the trick.
It affects .Available and .MoveNext as desired/expected.

Regards,
zxMarce.

On 2015-09-23 21:23, Benoît Minisini wrote:
Is it better with revision #7334?

Le 21/09/2015 15:08, zxMarce a écrit :
Benoît,

First off, with ODBC all .MoveXXX methods fail with "Result is forward only"
as expected, except .MoveFirst() and .MoveNext(). That is OK.

Second, I got the low level driver to return -1 as record count by
specifying different "TDS_Version" values in the connection string. For
example, "TDS_Version=7.2" returns zero, other versions return the expected
-1 (4.2, 7, 7.1, 7.3, etc). This is also OK, because it is
user-controllable.

But now that I have tested your mods, I must report the bad news: Both
.MoveNext and .Available properties always return FALSE, so there is no way
to cleanly exit a data retrieval loop.

I think the problem is in CResult.c at around line 145. That particular line
calls the driver's query_fill function but disregards the return value, so
the DB_ERROR, DB_OK and DB_NO_DATA return values are ignored.

If it helps, I cooked this mod in CResult.c, which made it work as expected
with ODBC, adjusting the return values of .MoveNext and .Available:

   //Next original line does not store the FILL call result,
   //therefore any call will be assumed OK even when the
   //ODBC driver says it failed.
   /*THIS->driver->Result.Fill(&THIS->conn->db,
                               THIS->handle,
                               pos,
                               THIS->buffer,
                               (pos > 0) && (pos ==
(DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
                              );*/

   //This next replacement line stores the FILL call result
   //in fillFailed so it can be checked later.
   int fillFailed = THIS->driver->Result.Fill(&THIS->conn->db,
                                          THIS->handle,
                                          pos,
                                          THIS->buffer,
                                          (pos > 0) && (pos ==
(DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))
                                         );

   //Next, check whether the FILL above succeeded or failed and
   //return proper states in Result.MoveNext and Result.Available.
   if (fillFailed)
   {
     THIS->available = FALSE;
     return TRUE;
   }

Finally, if I misinterpreted your intentions (yet again), please disregard
my suggestions.

Regards,
zxMarce.




------------------------------------------------------------------------------
Monitor Your Dynamic Infrastructure at Any Scale With Datadog!
Get real-time metrics from all of your servers, apps and tools
in one place.
SourceForge users - Click here to start your Free Trial of Datadog now!
http://pubads.g.doubleclick.net/gampad/clk?id=241902991&iu=/4140
_______________________________________________
Gambas-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gambas-devel
12
Loading...