SQLite 3.6.23.1 wrapper + connector

awishformore awishformore at gmail.com
Tue Jul 20 05:23:07 PDT 2010


Am 20.07.2010 08:56, schrieb Rory McGuire:
> On Tue, 20 Jul 2010 01:37:10 +0200, awishformore
> <awishformore at gmail.com> wrote:
>
>> Am 19.07.2010 21:34, schrieb Rory McGuire:
>>> On Mon, 19 Jul 2010 20:39:35 +0200, awishformore
>>> <awishformore at gmail.com> wrote:
>>>
>>>> Am 19.07.2010 17:50, schrieb Johannes Pfau:
>>>>> On 19.07.2010 00:44, awishformore wrote:
>>>>>> Hello there.
>>>>>>
>>>>>> I've converted the .h file of the latest SQLite version to .d and I
>>>>>> thought I'd let the world know (as suggested on IRC ;). Maybe it will
>>>>>> save someone some work.
>>>>>>
>>>>>> I've also written a nice connector class that wraps all the C
>>>>>> functions
>>>>>> for convenient use in your application. It's very basic, but I will
>>>>>> probably add more features (like automatic preparation of statements
>>>>>> and
>>>>>> automatic caching of several prepared statements) later.
>>>>>>
>>>>>> For the time being both files are included in the download:
>>>>>> http://nexunity.com/sqlite4d.rar
>>>>>>
>>>>>> I'm pretty new to this kind of stuff, so what I did to get it to work
>>>>>> was compiling the latest SQLite dll from source with dmc and then
>>>>>> link
>>>>>> the .obj file into my app ( http://nexunity.com/sqlite3.obj ).
>>>>>>
>>>>>> I'm sure there is a better way like compiling it as static lib (dmc
>>>>>> complained about no entry point) or having some kind of other file to
>>>>>> link into your app in order for it to compile and then use the dll. I
>>>>>> however couldn't figure it out and it works for me. Don't hesitate to
>>>>>> teach me nonetheless.
>>>>>>
>>>>>> Any kind of feedback is always appreciated.
>>>>>>
>>>>>> Greetings, Max.
>>>>> Nice work!
>>>>>
>>>>> I'm by no means an expert in d, but I wonder whether the following in
>>>>> the connector could cause troubles:
>>>>> --------------------------------------
>>>>> int open(string db_name)
>>>>> {
>>>>> return sqlite3_open_v2(cast(char*)db_name,
>>>>> --------------------------------------
>>>>>
>>>>> I always thought just casting to the pointer type isn't really safe. I
>>>>> guess you should use std.strings toStringz.
>>>>>
>>>>> (Off topic: std.strings toStringz uses the GC to store the c
>>>>> string. Now
>>>>> if I passed the returned pointer to a C function, but didn't store a
>>>>> reference in the D program, couldn't that c string get collected while
>>>>> the c library would expect it to be still there?)
>>>>
>>>> Hello there.
>>>>
>>>> I'm not an expert for anything related to programming, but I'm pretty
>>>> sure the string/char[]/char* isn't needed by SQLite after the function
>>>> returns, as you will always be using the sqlite4 object pointer.
>>>>
>>>> As for the issue about null-terminated C strings, I was initially
>>>> expecting this to not work, but tried anyway. I guess this has to do
>>>> with the fact that SQLite interprets the char* as UTF-8, which is what
>>>> D uses. With only a few lines of code, UTF-16 would also work with the
>>>> wrapper by the way, but I won't ever use it, so I didn't bother.
>>>>
>>>> I added quite a few unit tests to the file in order to verify stuff
>>>> like this and all of them seem to pass without the slightest issue and
>>>> there are no memory leaks either, so I'm assuming that at least the
>>>> basic functionality of my connector class is absolutely safe to use.
>>>>
>>>> The only thing I changed except for the unit tests is the
>>>> ptrtostr/ptrtoblob to access the pointer like an array instead of a
>>>> while loop and the else -> else static if(T==sqlite3_value*) to ensure
>>>> a valid parameter is always passed.
>>>>
>>>> Greetings, Max.
>>>>
>>>
>>> Did you test with a string that was not in the code itself, e.g. from a
>>> config file?
>>> String literals are null terminated so you wouldn't have had an issue if
>>> all your strings were literals.
>>> Utf8 doesn't contain the string length, so you will run in to problems
>>> eventually.
>>>
>>> You have to use toStringz or your own null terminator. Unless of course
>>> you know that the function will always be
>>> taking string literals. But even then leaving something like that up to
>>> the programmer to remember is not exactly
>>> fool proof.
>>>
>>> Enjoy.
>>> ~Rory
>>
>> Hey again and thanks for the hint. I tried finding something on the DM
>> page about string literals being null terminated and while the section
>> about string literals didn't even mention it, it was said some place
>> else.
>>
>> That explains why using string literals works even though I expected
>> it to fail. It's indeed good to know and adding std.string.toStringz
>> is probably a good idea ;). Thanks.
>>
>> Greetings, Max.
>
> sure, I must admit it is annoying when the same code can do different
> things just because of where the data came
> from. It would be easier to notice the bug if d never added a null on
> literals, but then there would also be a lot more
> usages of toStringz.
>
> I think if you want to test it you can do:
> auto s = "blah";
> open(s[0..$].dup.ptr); // duplicating it should put it somewhere else
> // just slicing will not test

When thinking about it, it makes sense to have string literals null 
terminated in order to have C functions work with them. However, I 
wonder about some stuff, for instance:

string s = "string";
// is s == "string\0" now?
char[] c = cast(char[])s;
// is c[6] == '\0' now?
char* p = s.ptr;
// is *(p+6) == '\0' now?

I think use of the zero terminator should be consistent. Either make 
every string (and char[] for that matter) zero terminated in the 
underlying memory for backwards compatibility with C or leave it to the 
user in all cases.

/Max


More information about the Digitalmars-d-announce mailing list