Das Forentreffen 2018 findet am 20./21. April in Dresden statt. Weitere Infos hier
Zur Homepage des Deutschsprachige Xbase-Entwickler e. V.
Xbase++-Wiki des Deutschsprachige Xbase-Entwickler e. V.

native PostgreSQL für Xbase++

Zugriff, Engines, Konvertierung. Von ADS über DBF bis zu SQL.

Moderator: Moderatoren

Antworten
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 10487
Registriert: Do, 16. Mär 2006 8:55
Wohnort: Hamburg

native PostgreSQL für Xbase++

Beitrag von AUGE_OHR » So, 26. Apr 2015 23:23

Vorgeschichte :
2001 hat Phil Ide die Idee PostgreSQL 7.3.1 libpq.DLL (PostgreSQL Access Library) API "direkt", statt ODBC, mit Xbase++ zu nutzten.
die Version von 2004 findet man bei Pablo Botella http://www.xbwin.com -> Newsgroup -> xfree.idep.resources -> 17.11.2008 -> XbPgSql

die ursprünglichen Version hat Pablo, mittels ot4xb, für PostgreSQL 8.3 weiter entwickelt.
http://www.xbwin.com -> Newsgroup -> xfree.resources -> 12.06.2008 -> libpq4xb

libpq4xb.prg ist nur der "Wrapper" welcher die libpq.DLL API Aufrufe bereit stellt. es ersetzt nur die

Code: Alles auswählen

DLLFUNCTION PQ***   USING STDCALL FROM libpq.dll
aufrufe mittels ot4xb.

die API Calls sind primär für die "Connection" verantwortlich welche sich von der API nicht(?) geändert hat. eine 32bit libpq.dll Version 7 funktioniert genau so die V8.x oder v9.x

p.s. es gehören zu libpq.dll auch noch folgende 32bit DLLs welche man in das Xbase++ EXE Verzeichnis kopiert werden sollte.

Code: Alles auswählen

libpq.dll
libeay32.dll
libiconv.dll
libintl.dll
libxml2.dll
libxslt.dll
ssleay32.dll
Hector hat bei Pablos unter http://www.xbwin.com -> Newsgroup -> xfree.resources -> 14.11.2012 -> Postgresql on Xbase++
seine Version als FinalPg.RAR veröffentlicht.

p.s. Frage von Hectors Version bitte direkt an Hector senden

---

für die Datenbank / Table Aktionen ist eine SQL-Query notwendig wie die CLASS PGSql von Phil Ide einsetzt.
zur "Auswertung" einer Antwort vom PostgreSQL Server hat Phil eine CLASS PGResult ( in PGSql.PRG ) entworfen.

die CLASS PGResult enthält Methoden zum "navigieren" in den "Tuples" von einem "ResultSet" wie man es z.b. für ein Browse ( myPGSQLBrowse.prg ) benötigt.
die CLASS PGResult wird von der CLASS PGSql "genutzt" und beide nutzen die API "Wrapper" Function (Phil oder Pablo)

sowohl Edgar, Hector und meine Version nutzen den API Wrapper von Pablo und die beiden Classen von Phil.
der Unterschied liegt nun den Browse Class Versionen wobei ich mich auf > 100000 "Tuples" konzentriert habe.

! Note : ein SELECT für eine SQL-Query ohne LIMIT kann zu (unerwünscht) grossen ResultSet führen was ein "navigieren"
in den "Tuples" sehr langsam machen kann wenn man z.b. die Method

Code: Alles auswählen

oResult:GoBottom()
verwendet.
auch führt ein hoher OFFSET in einer SQL-Query, bei einer grossen Table, zu einer deutlichen verlangsamung ...
wer Xbase++ v2.x mit PostgreSQL einsetzt sollte es mal mit einer grossen Table ausprobieren wie lange es dauert die zu öffnen

als Gegenprobe mit pgAdmin3.EXE -> Table auswählen -> Button in der Toolbar zum Anzeigen ( ohne LIMIT ).
dann "GoBottom" ( Strg-PgDn ) wie lange bis er die grosse Table aufbaut.

---

gegenüber DBF Dateien wird man feststellen das ein "GoBottom" zum "navigieren" bei grossen SQL-Table sehr langsam werden kann.
in der PostgreSQL v9.x Version habe ich nun die Function row_number() entdeckt welches ein "navigieren" mittels MOVE und OFFSET erheblich beschleunigen.

Code: Alles auswählen

   cVar1 := "BEGIN WORK;"

   IF SP_nVersion() < 9 // PostgreSQL Version
      cVar2 := "DECLARE MyCursor CURSOR FOR SELECT " + cField +;
              " FROM " + cTable + ;
              IF( EMPTY( cWhere ), "", " WHERE " + cWhere ) + ;
              " ORDER BY " + cOrder
   ELSE
      cVar2 := "DECLARE MyCursor CURSOR FOR SELECT " + cField +;
               ", row_number() OVER (ORDER BY " + cOrder + ")"+;
               " FROM " + cTable + ;
               IF( EMPTY( cWhere ), "", " WHERE " + cWhere ) + ;
               " ORDER BY " + cOrder
   ENDIF

   cVar3 := "MOVE  FORWARD " + cOffset + " IN MyCursor"
   cVar4 := "FETCH FORWARD " + cLimit  + " FROM MyCursor"
   cVar5 := "CLOSE MyCursor;"
   cVar6 := "COMMIT WORK;"
die PostgreSQL Function row_number() erzeugt nun eine "extra" Column im ResultSet welche man z.b. für DbPosition() benötigt.

p.s.
wie ich feststellen durfte scheint Alaska in der aktuellen Xbase++ v2.x bei der ISAM Emulation der vertikale Scrollbar in einem XbpBrowse() nicht funktioniert.
es funktioniert aber jetzt in der aktuellen Version wenn man "native" einen SQL-String direkt abschickt ... Alaska hat also dazugelernt ;)

---

ich habe das ganze nun in PGU.EXE eingebaut was so etwas wie DBU darstellen soll ( MODI STRUCT )

in PGU.EXE nutzen ich ein "native" Listview (DXE.LIB) statt eines TBrowse oder Xbpbrowse.
es wird dabei eine MultiCellgroup (Bestandteil von XbpBrowse) mit Listview "simuliert" wobei XbpBrowse ja kein Windows Control ist ;)
ebenfalls ist eine incrementelle Suche, auf den aktiven "Index", möglich. ein klick auf den "Header" ändert "on-fly" die Order ( Sortierung )

PGU.EXE hat eine DBF Import Schnittstelle, welche auch die ISAM Trigger von pgDBE unterstützt, jedoch werden keine zusätzlichen "__Index" Columne angelegt !!!
( was IMHO einfach Quatsch ist weil man einen IndexKey() Ausdruck auch "on-Fly" in ein SQL-String einbauen kann ... )

PGU.EXE kann auch Memo Type "C"*** sowie "V" für XbpBitmap() in Memo gespeichert ( mit FIELD->Bitmap := oBitmap:setBuffer() )
***ich "hörte" das eine Memo Type "C" nun ein anderes Format für FTS ( Fast Text Search ) verwendet was PGU.EXE z.Z. nicht unterstützt )

ich habe für die "Begrenzung" einen "Assisten" eingebaut um einen SQL-String zu erzeugen. er war mal für DBF gedacht ...

PGU.EXE findet man hier http://www.xbaseforum.de/viewtopic.php?f=16&t=6322
gruss by OHR
Jimmy

Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 10487
Registriert: Do, 16. Mär 2006 8:55
Wohnort: Hamburg

Re: native PostgreSQL für Xbase++

Beitrag von AUGE_OHR » Di, 11. Apr 2017 2:05

hier der Source von Phil Ide und die ot4xb Version von Pablo im Original
xbpgsql.zip
Phil Ide
(38.57 KiB) 34-mal heruntergeladen
libpq4xb.zip
Pablo ot4xb Version
(9.99 KiB) 31-mal heruntergeladen
gruss by OHR
Jimmy

ramses
1000 working lines a day
1000 working lines a day
Beiträge: 669
Registriert: Mi, 28. Jul 2010 17:16

Re: native PostgreSQL für Xbase++

Beitrag von ramses » Mo, 01. Mai 2017 21:39

Hallo Jimmy

Beide Beispiele sind unvollständig.

Weisst du woher die nicht vorhandene aber scheinbar erforderliche #include "piCommon.ch" kommt?

Gruss Carlo

Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 10487
Registriert: Do, 16. Mär 2006 8:55
Wohnort: Hamburg

Re: native PostgreSQL für Xbase++

Beitrag von AUGE_OHR » Mo, 01. Mai 2017 22:03

ramses hat geschrieben:Beide Beispiele sind unvollständig.

Weisst du woher die nicht vorhandene aber scheinbar erforderliche #include "piCommon.ch" kommt?
:shock:
die "piCommon.ch" ist von Phil Ide und dazu gehört auch eine "C" die als Object dabei sein müsste.
psqlsupp.zip
(2.55 KiB) 28-mal heruntergeladen
IMHO reicht das Object denn auch die Datei hrtimer.prg im Project.XPJ ist nicht notwendig.
gruss by OHR
Jimmy

ramses
1000 working lines a day
1000 working lines a day
Beiträge: 669
Registriert: Mi, 28. Jul 2010 17:16

Re: native PostgreSQL für Xbase++

Beitrag von ramses » Di, 02. Mai 2017 8:00

Hallo Jimmy

Danke füe die Include Datei. Damit lassen sich nach dem entfernen der hrtimer Klasse die Bespiele nun compilieren und linken.


Gruss Carlo

Benutzeravatar
Hans Zethofer
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 243
Registriert: Fr, 27. Jan 2006 9:29
Wohnort: 2700 Wiener Neustadt
Kontaktdaten:

Re: native PostgreSQL für Xbase++

Beitrag von Hans Zethofer » Di, 02. Mai 2017 13:47

hier noch die 'hrtimer() Klasse' falls wer diese benötigt! :wink:
Dateianhänge
hrtimer.zip
(4.09 KiB) 16-mal heruntergeladen
_____________
lg
Hans

Benz
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 415
Registriert: Mo, 30. Mai 2011 15:06

Re: native PostgreSQL für Xbase++

Beitrag von Benz » Mi, 26. Jul 2017 18:02

Hi mit welcher Xbase++ Version funktioniert das? nur mit 2.0 oder auch mit 1.90355?

Ich bekomme nämlich die folgende Fehlermeldung (benutze Version 1.90355):

Dabei ist artikelgruppennr eine Spalte in meiner Datenbank. Zuvor kommt die Meldung, dass meine Tabelle 4 Zeilen hat, ich habe also Zugriff auf den Postgre.
Im Programm ist die Makefieldblock so definiert:

Code: Alles auswählen

Function MakeFieldBlock( o, c )
   return {|| o:&(c) }
   
Weshalb ich versucht habe auf oPGresult:artikelgruppennr zuzugreifen, aber ohne Erfolg. Kann es überhaupt gehen, dass man einem Objekt Eigenschaften hinzufügt, ohne dass diese zuvor in der KLasse definiert wurden (also artikelgruppennr in dem Fall). Wie komme ich an die Inhalte ran, wenn nicht wie hier gezeigt?
Dateianhänge
fehler.PNG
fehler.PNG (13.6 KiB) 274 mal betrachtet
Zuletzt geändert von Benz am Mi, 26. Jul 2017 18:24, insgesamt 2-mal geändert.

Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 10487
Registriert: Do, 16. Mär 2006 8:55
Wohnort: Hamburg

Re: native PostgreSQL für Xbase++

Beitrag von AUGE_OHR » Mi, 26. Jul 2017 18:21

Benz hat geschrieben:
Mi, 26. Jul 2017 18:02
Hi mit welcher Xbase++ Version funktioniert das? nur mit 2.0 oder auch mit 1.90355?
da es sich um "pure" Xbase++ handelt ist es Versions unabhängig :D
gruss by OHR
Jimmy

georg
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1910
Registriert: Fr, 08. Feb 2008 22:29

Re: native PostgreSQL für Xbase++

Beitrag von georg » Mi, 26. Jul 2017 20:46

Hallo, Benz -


ich kenne diese Klasse nicht, aber Deine Abfrage scheint mir falsch zu sein.

Enthält das Objekt tatsächlich eine Instanzvariable, die wie das Tabellenfeld heisst (d.h. für jedes Feld eine entsprechende Instanzvariable), oder musst Du über oCursor:Fieldget("feldname") auf das Feld zugreifen?
Liebe Grüsse aus der Eifel,

Georg

Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 10487
Registriert: Do, 16. Mär 2006 8:55
Wohnort: Hamburg

Re: native PostgreSQL für Xbase++

Beitrag von AUGE_OHR » Mi, 26. Jul 2017 21:30

Benz hat geschrieben:
Mi, 26. Jul 2017 18:02
Hi mit welcher Xbase++ Version funktioniert das? nur mit 2.0 oder auch mit 1.90355?
die Function ist ja im Code von Phil Ide und der ist von 2004 also noch Xbase++ v1.8x
Benz hat geschrieben:

Code: Alles auswählen

Function MakeFieldBlock( o, c )
   return {|| o:&(c) }   
Weshalb ich versucht habe auf oPGresult:artikelgruppennr zuzugreifen, aber ohne Erfolg.
hm ... hast du den schon ein Resultset sprich eine Query abgeschickt ?

Code: Alles auswählen

oBr:addColumn( MakeFieldBlock( oPGResult, cField ), , cField )
gruss by OHR
Jimmy

Benz
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 415
Registriert: Mo, 30. Mai 2011 15:06

Re: native PostgreSQL für Xbase++

Beitrag von Benz » Do, 27. Jul 2017 8:41

@georg: Nun das ist ja das Beispiel, das ich von Jimmy hier in diesem Beitrag heruntergeladen habe. Ich fand genau das ja so seltsam, weil genau das macht ja die Funktion MakeFieldBlock, Sie ruft die Felder als Instanzvariablen des Objektes auf.

Code: Alles auswählen

/*****************************
* Source : xuguk.prg
* System : <unkown>
* Author : Phil Ide
* Created: 04/02/2003
*
* Purpose:
* ----------------------------
* History:
* ----------------------------
*    04/02/2003 14:47 PPI - Created
*****************************/

#include "xbp.ch"
#include "Common.ch"
#include "Appevent.ch"
#include "simpleio.ch"

#define CRLF Chr(13)+Chr(10)

proc appsys(); return
proc dbesys(); return

proc main(cQueryFile, dbName, user, pwd)
   local r, c, c1 := 0, x
   local cConnect
   local oPG
   local cSQL
   local oRes
   local aF
   local i, n
   local aFNames
   local nH
   local oHRTimer := HRTimer():new()

   default cQueryFile to 'query0.sql'

   cConnect := "host=localhost dbname="+dbName+" user="+user+" password="+pwd
   oPG := PGSql():new()
   if FExists(cQueryFile) .and. oPG:connect( cConnect )
      cSQL := LoadQuery(cQueryFile)
      oHRTimer:start()
      if oPG:exec(cSQL)
         oHRTimer:stop()
         oRes := oPG:result

         msgBox("Done!"+CRLF+;
                "rows="+LTrim(Str(oRes:rows))+CRLF+;
                "cols="+LTrim(Str(oRes:cols))+CRLF+;
                "message="+var2char(oRes:message)+CRLF+;
                "Time="+LTrim(Str(oHRTimer:duration,15,12))+" secs","SQL Test")


         // uncomment this block to test using dynamic column names
         // you'll also need to change the column names used (_header, _name) to
         // whatever is suitable for your query.
         //
         /*
         aFNames := oRes:getFieldnames()

         // the lazy way...
         //
         nH := FCreate('result1.txt')
         while !oRes:eof()
            cLine := ''
            for i := 1 to Len(aFNames)
               cLine += oRes:&(aFNames[i])+'  '
            next
            FWrite( nH, cLine+CRLF )
            oRes:skip() // could be oRes:skip(-1)
         enddo
         FClose(nH)

         oRes:goTop() // go top!

         // the hard way...using dynamic column names
         //
         nH := FCreate('result2.txt')
         while !oRes:eof()
            cLine := oRes:_header+'  ' // field names are NOT case-sensitive!
            cLine += oRes:_name+CRLF
            FWrite( nH, cLine )
            oRes:skip()
         enddo
         FClose(nH)

         nH := FCreate('result3.txt') // now produce results in reverse order
         while !oRes:bof()
            cLine := ''
            for i := 1 to Len(aFNames)
               cLine += oRes:&(aFNames[i])+'  '
            next
            FWrite( nH, cLine+CRLF )
            oRes:skip(-1)
         enddo
         FClose(nH)
         */
         Browse(oRes)

         oRes:destroy()
      endif
      oPG:disconnect()
   endif
   return

FUNCTION GuiStdDialog( cTitle )
      LOCAL oDlg
      local aSize := AppDeskTop():currentSize()
      local aPos  := AppDeskTop():currentSize()

      aSize[1] *= 0.90
      aSize[2] *= 0.75

      aPos[1] := (aPos[1] - aSize[1])/2
      aPos[2] := (aPos[2] - aSize[2])/2

      DEFAULT cTitle TO "Standard Dialog Window"

      oDlg          := XbpDialog():new( ,,aPos, aSize,, .F. )
      oDlg:icon     := 1
      oDlg:taskList := .T.
      oDlg:title    := cTitle
      oDlg:create()
      oDlg:drawingArea:setFontCompoundName( "8.Helv" )


   RETURN oDlg

Function Browse(oPGResult)
   local oDlg := GuiStdDialog("SQL Browse")
   local oBr := myPGSQLBrowse():new(oDlg:drawingArea):create()
   local i
   local cField

   local nEvent
   local mp1
   local mp2
   local oXbp

   oBr:setup(oPGResult)

   altd()

   for i := 1 to oPGResult:cols
      cField := oPGResult:fname(i-1)
      oBr:addColumn( MakeFieldBlock( oPGResult, cField ), , cField )
   next

   oDlg:drawingArea:resize := {|mp1,mp2,obj| obj:childlist()[1]:setSize(mp2) }
   oDlg:show()
   oBr:show()
   SetAppFocus(oBr)
   While nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   Enddo
   oDlg:destroy()
   return Nil

Function MakeFieldBlock( o, c )
   return {|| o:&(c) }


Function LoadQuery(cFile)
   local cData := MemoRead(cFile)
   local cComment
   local nStart, nEnd

   if (nStart := At('/*',cData)) > 0
      if (nEnd := At('*/',cData,nStart+2)) > 0
         cComment := AllTrim(SubStr( cData, nStart+2, nEnd-(nStart+3) ))
         if !Empty(cComment)
            msgBox(cComment,"Query Comment - "+cFile)
         endif
      endif
   endif
   return cData
@Jimmy: cQueryFile ist ja die Variable, in die ich einen Dateinamen beim Aufruf der Main schreibe. In meinem Fall hatte ich da die queryxy.sql.

Der Inhalt dieser SQL-Datei ist:

Code: Alles auswählen

SELECT artikelgruppennr, artikelnr, kundennr, faktor, einheit_preis, preis, mindestpreis, kundengesamtrabatt, id, artikelgruppen_id, volberechnung, preisgestaltung FROM public.artikelpreise;
Und in der Zeile hier:

Code: Alles auswählen

         msgBox("Done!"+CRLF+;
                "rows="+LTrim(Str(oRes:rows))+CRLF+;
                "cols="+LTrim(Str(oRes:cols))+CRLF+;
                "message="+var2char(oRes:message)+CRLF+;
                "Time="+LTrim(Str(oHRTimer:duration,15,12))+" secs","SQL Test")
... werden ja die Zeilen ausgegeben, die in der Datenbanktabelle vorhanden sind. (Ichh abe noch die Spalten hinzugefügt) und das Objekt gibt beides korrekt zurück. Das heißt die Verbindung zur Datenbank und die Verbindung zur Datenbanktabelle ist da. Nur scheint der Aufruf der Tabellenspalten nicht als Instanzvariablen des Resultobjektes zu funktionieren. An dem Code, den ich hier heruntergeladen habe, habe ich nichts geändert. Ich füge das ganze Paket mal als ZIP an (ohne die .ch-Dateien, die sind im Compilerverzeichnis).
Dateianhänge
psgqltest.rar
(113.41 KiB) 5-mal heruntergeladen

Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 10487
Registriert: Do, 16. Mär 2006 8:55
Wohnort: Hamburg

Re: native PostgreSQL für Xbase++

Beitrag von AUGE_OHR » Sa, 29. Jul 2017 4:36

hi,

ich habe es mit der v1.9.355 getested und kann dein Resultat bestätigen ... da war doch was ... :?:
als Phil den Code geschrieben hat war es die Xbase++ v1.8x und er hat NoIvarCallBack(x)/NoIvar() benutzt.

das Verhalten hat sich dann unter v1.9x verändert und er "springt" nicht mehr auf den Code an [-X
neverless ist das nicht der richtige Weg für ein o:Datalink mit PostgreSQL.

der o:Datalink ist für das Browse was ein Resultset anzeigt. Die neue Method muss also in die PgResult CLASS.

Code: Alles auswählen

- PGSQL.PRG ----------------------------------------------------------------
METHOD PGResult:DataBlock( cFname )       // Jimmy : for PGbrowse Codeblock
LOCAL nPosi   := 0
LOCAL xRet    := ""

   DEFAULT cFname TO ""

   IF VALTYPE( cFname ) = "N"
      // this IS 0 Zero - based !!!
      nPosi := cFname - 1
   ELSE
      // NOT 0 Zero - based any more
      nPosi := PQfnumber( ::resID, cFname )
   ENDIF

   IF nPosi >= 0
      // NOT 0 Zero - based any more
      xRet := ::getValue( ::curTuple, nPosi )
   ENDIF
RETURN xRet 
der Aufruf wäre dann also so

Code: Alles auswählen

Function MakeFieldBlock( o, c )
*  return {|| o:&(c) }
RETURN { || o:DataBlock( c ) }
anbei erweiterte PgSQL.PRG
PGSQL.ZIP
pure Xbase++ Source
(3.59 KiB) 8-mal heruntergeladen

p.s. die bei Phil enthaltene libpq.dll funktioniert zwar aber man sollte die durch die aktuelle ersetzten !
gruss by OHR
Jimmy

Antworten