SQLite [Working Code]

Linux specific forum
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

SQLite [Working Code]

Post by Num3 »

This code works fine if i just run it under the compiler "PBCompiler sql_linux.pb", but if i try to run the executable it says "Unable to make Query" (program detects it cannot make a query)

I assume the table has to be in some system folder where it can be reached, but i don't know which...

It's kind of stupid the sqlite.so has to be in the lib folder and Linux does not search in the program executable directory itself :(

Help, anyone :roll:

Code: Select all

#SQLITE_OK =0              ; Successful Result

Procedure.l SQLite_Get_Table(DB_Handle.l, SQLString.s,  * Rows,  * Cols)
  Shared Result.l
  Shared LResultsPtr.l
  Shared LRows.l
  Shared LCols.l
  
  Result  = CallFunction(0, "sqlite_get_table", DB_Handle, SQLString, @LResultsPtr, @LRows, @LCols, 0)
  
  If Result  = #SQLITE_OK
    ; return number of rows/columns
    PokeL(*Rows,  LRows)
    PokeL(*Cols,  LCols)
    
    ; redimension results array (clears data)
    Dim DBData.s(LRows, LCols - 1)
    ; copy data into array
    Address.l  = LResultsPtr
    AddrInc.l  = LCols  * 4
    For Row.l  = 0 To LRows
      For Col.l  = 0 To LCols - 1
        DBData(Row,  Col) = PeekS(PeekL(Address  + Col  * 4))
      Next
      Address  + AddrInc
    Next
    ; free table memory
    CallFunction(0,  "sqlite_free_table", LResultsPtr)
  EndIf
  
  ProcedureReturn Result
EndProcedure

errormsg.s=""
If OpenLibrary(0,"sqlite.so")

  db=CallFunction(0,"sqlite_open","test.db",0,errormsg)
  
  If db

    SQL.s  = "SELECT id, url, site, category FROM bookmarks WHERE category LIKE 'delp%'"
    Result  = SQLite_Get_Table(db, SQL, @Rows, @Cols)
    
    If Result  = #SQLITE_OK
      ; get the results
      ; display number of rows/columns
      a$=""
      a$+ "Rows = "  + Str(Rows)+Chr(10)
      a$+ "Columns = "  + Str(Cols)+Chr(10)+Chr(10)
      
      ; display column headers
      For Col.l  = 0 To Cols  - 1
        a$+ DBData(0, Col)+Chr(10)
      Next
      ; display returned rows
      For Row.l  = 1 To Rows
        a$+ "--------------" +Chr(10)
        For Col  = 0 To Cols  - 1
          a$+ DBData(Row, Col)+Chr(10)
        Next
      Next

      CallFunction(0,"sqlite_close",db)
      MessageRequester("Result",a$)
      a$=""
      
    Else
      MessageRequester("Error","Unable to make query"+Chr(10)+errormsg)
    EndIf
    
    
  Else
    MessageRequester("Error","Unable to open database"+Chr(10)+errormsg)
  EndIf
  

Else
  MessageRequester("Error","Unable to open library")
EndIf
El_Choni
TailBite Expert
TailBite Expert
Posts: 1007
Joined: Fri Apr 25, 2003 6:09 pm
Location: Spain

Post by El_Choni »

I think this should work in Linux too, and could help you find the problem:

Code: Select all

  Result  = CallFunction(0, "sqlite_get_table", DB_Handle, SQLString, @LResultsPtr, @LRows, @LCols, @zErrMsg)
  If Result=#SQLITE_OK
    ; code
  ElseIf zErrMsg
    Debug PeekS(zErrMsg)
  EndIf
El_Choni
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Post by Num3 »

Found it!

Thanks El_Choni!!!



Ok seems Linux is dumb...

This is how it goes...

sqlite.so goes into lib folder
test.db goes into the root folder

and the code works just fine :P

Is there any way to force Linux to read a specific directory for those files...
(without being hardcoded)

*G* i really need GetCurrentDirectory() ...
freak
PureBasic Team
PureBasic Team
Posts: 5946
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Num3 wrote:*G* i really need GetCurrentDirectory() ...
You can read the current directory from the PWD environment variable like this:

Code: Select all

Procedure.s GetEnv(Variable$)
  Protected *Environ.LONG

  !extrn _environ
  !mov eax, [_environ]
  !mov [esp+4], eax

  Variable$ + "="

  While *Environ\l <> 0
    If CompareMemoryString(@Variable$, *Environ\l, 0, Len(Variable$)) = 0
      ProcedureReturn PeekS(*Environ\l + Len(Variable$))
    EndIf
    *Environ + 4
  Wend

  ProcedureReturn ""
EndProcedure

OpenConsole()
PrintN("Current Directory: "+GetEnv("PWD"))
CloseConsole()

End
hum, i better put that in Tips&Tricks too

Timo
quidquid Latine dictum sit altum videtur
StanDan
User
User
Posts: 57
Joined: Sun Feb 26, 2006 3:43 am
Location: Missouri, United States

Another solution

Post by StanDan »

I'm absolutely brand new to the site and to PureBasic (I just bought it today.) However, I found this discussion very useful and since I have a different solution that seems to work I thought I'd post it. The code that uses environ only gets the present working directory. If an application is being called from the shell then the PWD environment variable may not reflect the directory that the executable is in.

This was my problem. Linux places your actual executable name on the stack when your program starts running but I could find no way to insert assembly that early in program execution. So here's my (hack) solution (it may even be hilariously wrong, but it seems to work.) I'm guessing you could call getcwd_() or get_current_dir_name_() but I developed this using the demo version and couldn't get it to work. And that wouldn't return the right value anyway.

Code: Select all

; Get the program's process ID
Procedure.w GetPid()
    !mov eax, 20
    !int 0x80
    ProcedureReturn
EndProcedure

; Read the symlink in mypath and return the file pointed to.
Procedure.s ReadLink(mypath.s)
    mybuf.s = ""
    mybufsize.l = 512
    mybytesread.l = 0
    mybuf = Space(mybufsize);

    !mov eax, 85
    !mov ebx, [esp]
    !mov ecx, [esp+4]
    !mov edx, [esp+8]
    !int 0x80
    !mov [esp+12], eax

    mybuf = RTrim(mybuf)
    If mybytesread > 0
        ProcedureReturn mybuf
    Else
        ProcedureReturn ""
    EndIf
EndProcedure

; Get my program filename and path.
Procedure.s GetExe()
    pid.w = GetPid()
    procpath.s = "/proc/"+Str(pid)+"/exe"
    exe.s = ReadLink(procpath)
    ProcedureReturn exe
EndProcedure
So a session with this and GetEnv both trying to load a function from a library in the program's path looks like this.

Code: Select all

jonathan@kosmic:~/dev/learning$ make
gcc -Wall -pedantic -fPIC -c mystuff.c
gcc -shared -Wl,-soname,libmystuff.so.1 -o libmystuff.so mystuff.o -lc
pbcompiler -e testso.exe -q testso.pb
jonathan@kosmic:~/dev/learning$ ls
Makefile  libmystuff.so  mystuff.c  mystuff.o  testso.exe  testso.pb
jonathan@kosmic:~/dev/learning$ ./testso.exe
GetEnv() method is: /home/jonathan/dev/learning/libmystuff.so
GetExe() method is: /home/jonathan/dev/learning/libmystuff.so
libmystuff.so:someFunc() called with 'Hello World!'!
Function returned: 7357
jonathan@kosmic:~/dev/learning$ cd ..
jonathan@kosmic:~/dev$ learning/testso.exe
GetEnv() method is: /home/jonathan/dev/libmystuff.so
GetExe() method is: /home/jonathan/dev/learning/libmystuff.so
Sorry can't load the library!
jonathan@kosmic:~/dev$ cd ..
jonathan@kosmic:~$ /home/jonathan/dev/learning/testso.exe
GetEnv() method is: /home/jonathan/libmystuff.so
GetExe() method is: /home/jonathan/dev/learning/libmystuff.so
Sorry can't load the library!
The c file looks like:

Code: Select all

#include <stdio.h>

int someFunc(char *str);
int someFunc(char *str) {
    fprintf(stderr, "libmystuff.so:someFunc() called with '%s'!\n", str);
    return 7357;
}
And the PB code looks like:

Code: Select all

myso_pwd.s = GetEnv("PWD") + "/libmystuff.so"
myso_link.s = GetPathPart(GetExe()) + "libmystuff.so"

OpenConsole()
PrintN("GetEnv() method is: "+myso_pwd)
PrintN("GetExe() method is: "+myso_link)

hello$ = "Hello World!"

If OpenLibrary(0, myso_pwd)
  *someFunc = IsFunction(0, "someFunc")
  If *someFunc
    res.l = CallFunctionFast(*someFunc, hello$)
    PrintN("Function returned: "+Str(res))
  EndIf
  CloseLibrary(0)
Else
    PrintN("Sorry can't load the library!")
EndIf
So you can see that this method will always return the actual directory your EXE is in, not just the directory it was called from. I hope this is helpful to someone, I spent quite some time figuring it out.

I gather from this discussion:

viewtopic.php?t=19639

That PB4 will have a built in keyword to do this. Way to go! Whoohoo!
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

ts-soft's cross-platform GetCurrentDir() can be found here (last post):

viewtopic.php?t=17817
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

Straker wrote:ts-soft's cross-platform GetCurrentDir() can be found here (last post):

viewtopic.php?t=17817
[EDIT]

Whoa - I didn't really how old this thread really was because of the recent post.

Anyway, welcome to PureBasic StanDan!
StanDan
User
User
Posts: 57
Joined: Sun Feb 26, 2006 3:43 am
Location: Missouri, United States

Thanks Straker!

Post by StanDan »

This was exactly what I needed. I did search the forums like crazy but never found this little gem.

I was just doing a search trying to figure out how to typecast a pointer to a string (which doesn't seem possible.) When I noced you'd replied, and his answer accomplishes EXACTLY what I was having a problem with (notably, getting the return value from POSIX getcwd_(0,0) (or GNU_SOURCE__'s get_current_dir_name_()) which returns a pointer which is actually a string.)

I've been declaring two variables when I need to do this (a string and a pointer). Then using inline assembly to copy the pointer value into the string pointer on the stack (and possibly creating a memory leak, I have no idea how PureBasic handles memory.) There must be a better way! Gosh I hate being a newbie. Learning a new language is both exciting and humbling at the same time.

I know you can do this:

Code: Select all

a.s = "some string here"
PrintN(Hex(@a))
But for some reason you can't do this (syntax error):

Code: Select all

a.s = "some string here"
b.l = @a
c.s = "some other string"
@c = b
Memory management is probably the answer why. PureBasic would not deallocate the string "some other string\0" pointed to by @c before it's assigned the address of the string pointed to by @a.

But I don't know JACK so that could be utterly insane and offbase. I also tried using pointer notation *b = @a and that doesn't work either.

Anyway, thanks again for the help. I can now put this thorny problem down and go back to actually doing something productive. :D
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

I am not sure, but I think you are making this harder than it is.

To get the string from a returned pointer, use:

Code: Select all

a.s = PeekS(*pointer)
StanDan
User
User
Posts: 57
Joined: Sun Feb 26, 2006 3:43 am
Location: Missouri, United States

doh!

Post by StanDan »

I saw Peek and Poke in the docs, never put 2 and 2 together. Gosh, I love PureBasic. I knew there was something I was missing -- given the language, it had to be easier. Thanks again man.
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

No problem. I use PeekS() mostly when using the CallFunction() functions, because they return longs, even if the DLL function they are calling returns strings. So if you have issues with calling DLL functions, remember PeekS()!

Good luck.
Post Reply