Page 1 of 1
Switching Client Encoding for PostgreSQL
Posted: 08.11.2011, 15:08
by cnliou
This is the patch to fix the flaw as mentioned by myself at
http://zeos.firmos.at/viewtopic.php?p=13794#13794
I have just fixed the flaw and the result looks fine. Summary of the rework:
- Ignore entry "codepage=..." in TZConnection.Properties. That is said, setting this entry does not change anything any more.
- The correct way to switch code page is calling TZConnection.SetCodePage(String). You can make the call any time after TZConnection is connected.
- The cycle of "disconnect - reset code page - connect" is no longer necessary.
Four files are affected as attached.
I guess the code base I worked on is version 6.6 but I am not absolutely sure. I do not know where to download the most up-to-date branches through svn and then work on that version(s)/branche(s). Besides, because I only have BCB 6, I hope some one with patching privileges can manually patch these files for me.
Regards,
CN
Posted: 08.11.2011, 23:09
by mdaems
cnliou,
Seems to me we have to make sure this new interface function can be implemented for all databases.
And if that's done we should make it react to an entry "codepage=..." in TZConnection.Properties the way it does now.
Have you done this check already? Not sure if all databases accept this change after the connection is effectively made.
If some databases do not accept this, I'd even say this change is against the basic principle that dbc interface functions should work for all databases in a similar way.
In that case you still have 2 options available
- Add it to the postgres connection dbc interface, so postgres users can use it if they want.
- Leave it as it is now.
Your opinion?
Mark
Posted: 09.11.2011, 03:38
by cnliou
I have just quickly reviewed mysql, sqlite, and interbase6. It appears to me that none of them reacts to the change of "codepage=..." after connection, and sqlite even completely ignores this parameter.
It will be fine for myself to make SetCodePage() available only to PostgreSQL. However, I don't know how to directly call zdbc procedures from my application. If you can show me how, I can try exporting SetCodePage() to zdbc instead of TZConnection.
Regards,
CN
Posted: 09.11.2011, 10:12
by cnliou
cnliou wrote:It will be fine for myself to make SetCodePage() available only to PostgreSQL. However, I don't know how to directly call zdbc procedures from my application. If you can show me how, I can try exporting SetCodePage() to zdbc instead of TZConnection.
I have read
http://zeos.firmos.at/kb.php?mode=artic ... 22ea6132bd
and
http://zeos.firmos.at/kb.php?mode=artic ... 22ea6132bd
If I understand correctly, this is not a good way for me to go
Code: Select all
...
var
Connection: IZConnection;
Params: TStrings;
...
Connection := DriverManager.GetConnectionWithParams(...)
because, if my understanding is right, my application will create two connections: TZConnection and IZPostgreSQLConnection. Components such as TZQuery connect to server via TZConnection. IZPostgreSQLConneciton is for fast direct access to server. The problem is that switching code pages in IZConnection does not affect TZConnection and accordingly TZQuery.
As to the tutorial
Code: Select all
IZMySQLConnection = interface (IZConnection)
function Ping(...);
function Kill(..);
end;
, I might be able to move SetCodePage() as the example demonstrates:
Code: Select all
IZPostgreSQLConnection = interface (IZConnection)
SetCodePage();
end;
My problem is I don't know how to implement a function that returns IZPostgreSQLConnection using TZConnection and its properties/functions similar to the tutorial
Code: Select all
MyConnection: IZMySQLConnection;
MyConnection := DriverManager.CreateConnection(Url) as IZMySQLConnection;
MyConnection.Ping;
I have also noticed that interbase6 has huge number of parameters to be passed to server. As a result, implementing procedures one by one for all these parameters is impractical. Some mechanism that reacts to the changes of TZConnection.Properties seems to be more practical in terms of interbase 6. On the other hand, such mechanism requires iterating the entries in TZConnection.Properties in order to fire the appropriate procedures. Iteration is very inefficient, especially for those server applications that use zeoslib.
Limited by little skill of Delphi and knowledge of so many servers other than PostgreSQL, I hope someone can help or even better, take over this enhancement/repair.
Regards,
CN
Posted: 09.11.2011, 21:55
by mdaems
cnliou,
I think you better read this article :
http://zeos.firmos.at/kb.php?mode=article&k=14
It's the SQLite specific implementation of encryption functionionality. It shows how you can use code as
Code: Select all
//by krassonkel
var Conn: IZSQLiteConnection;
Pass: string;
begin
Pass := 'secret';
Conn := (ZConnection1.DbcConnection as IZSQLiteConnection);
Conn.GetPlainDriver.ReKey(Conn.GetConnectionHandle, PChar(Pass), StrLen(PChar(Pass)));
end;
to call db-specific dbc layer functions.
For other databases:
- I know mysql API supports mysql_set_character_set().
- Firebird needs to know the charset at connect time
- Oracle seems to have the same behaviour as FB (connect time)
- SQLite : looks like it's treating strings like raw memory buffers and doesn't link charsets to the data (I might be wrong, I only scanned the API documentation briefly)
What do you think? Can you manage this change for the postgres driver. Maybe I'll give the mysql driver a try afterwards, but I never worked with charsets before. So I need a tester for that...
Mark
Posted: 10.11.2011, 13:32
by cnliou
mdaems wrote:I think you better read this article :
http://zeos.firmos.at/kb.php?mode=article&k=14
It's the SQLite specific implementation of encryption functionionality. It shows how you can use code to call db-specific dbc layer functions.
What do you think? Can you manage this change for the postgres driver. Maybe I'll give the mysql driver a try afterwards, but I never worked with charsets before. So I need a tester for that...
Adding SetCodePage() only in ZDbcPostgreSQLConnection.pas requires me the least effort but BCB 6 can't compile the following snippet of my application
Code: Select all
const_cast<IZPostgreSQLConnection *>(ZConnection->DbcConnection)->SetCodePage(PgClientEncoding);
and complains:
Code: Select all
Cannot cast from 'const _di_IZConnection' to 'IZPostgreSQLConnection *'
I have spent a whole day figuring out the correct way of casting constant interface to another interface in C++Builder, but to no avail.
Posted: 10.11.2011, 14:00
by mdaems
Don't know BCB6... So this may be a stupid response :-S
Using the 3 step cast as in the example doesn't work either?
- Create IZPostgresSQLConnection object variable
- Assign this variable a value by casting
- Use the var->SetCodePage() function.
Posted: 10.11.2011, 14:32
by cnliou
mdaems wrote:
Using the 3 step cast as in the example doesn't work either?
- Create IZPostgresSQLConnection object variable
- Assign this variable a value by casting
- Use the var->SetCodePage() function.
No, this can't compile:
Code: Select all
IZPostgreSQLConnection *c=static_cast<IZPostgreSQLConnection *>(ZConnection->DbcConnection);
I don't want to see the enhancement be delayed by me. It requires us to send the command "SET CLEINT_ENCODING TO <character set>" to PostgreSQL in order to tell it to switch code page. I notice that mySQL does the same only with minor difference in the command. How about we go this way:
You add SetCodePage() in ZDbcMySQLConnection.pas and test it. If it works for mySQL, then I follow your code and add it in ZDbcPostgreSQLConnection.pas. Then you commit both patches without test run for PostgreSQL driver.
My another concern is the many code bases we have now: There currently are 6.6.x and 7.x trunk, branch, testing, etc. I don't know exactly which code bases need to be patched. Besides, I have only BCB 6, which is incompatible with unicode new features. As such, I would much more appreciate if you can arrange to add this functionality to both mySQL and PostgreSQL and commit the changes. Then I will try to test 6.x in PostgreSQL.