PostgreSQL Externe C-Funktion



  • DirkB schrieb:

    Ich würde die 1 beim %*1[ ,] noch weglassen.
    Das macht es etwas flexibler bezüglich der Anzahl der Trennzeichen

    Ich nicht. Die 1 wegzulassen hieße, dass aufeinanderfolgende "," nicht als Fehler erkannt würden, was etwa bei "11 22,33 44,,55 66" u.ä. wohl kaum wünschenswert sein dürfte. Aufeinanderfolgende Leerzeichen werden auch mit 1 schon berücksichtigt und führen nicht zum Abbruch. "Flexibel" ist immer subjektiv, will man bei Vorschriftenverstoß irgendwie weitermachen (um am Ende ein zweifelhaftes Ergebnis zu erhalten) oder will man abbrechen und über Vorschriftenverstoß informieren.



  • Nach meiner letzten Veränderung sieht der Code jetzt so aus.

    //------------------------------------------------------------------------------
    
    double return_of_spl_string(const char*s)
    {
      int n=0;
      double d;
      double sum;
      s=strchr(s,'\'')+1;
      while( 1==sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) ) 
      {
    	  sum += pow(d, 2);  
      }
      return pow(sum, 0.5);
    }
    
    //------------------------------------------------------------------------------
    PG_FUNCTION_INFO_V1(FCT_Norm);
    PGDLLEXPORT 
    Datum
    FCT_Norm(PG_FUNCTION_ARGS)
    	{
    
        FuncCallContext     *funcctx;
        int                  call_cntr;
        int                  max_calls;
        char       *_str = PG_GETARG_CSTRING(0);
    
    	if (SRF_IS_FIRSTCALL())
    
    			{
    			MemoryContext   oldcontext;
    
    			//create a function context for cross-call persistence 
    			funcctx = SRF_FIRSTCALL_INIT();
    
    			// switch to memory context appropriate for multiple function calls 
    			oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    
    			/* total number of tuples to be returned */
    			funcctx->max_calls = PG_GETARG_UINT32(0);
    			MemoryContextSwitchTo(oldcontext);
    
    			}	
    
    				call_cntr = funcctx->call_cntr;
    				max_calls = funcctx->max_calls;
    				funcctx = SRF_PERCALL_SETUP();
    
    				while(call_cntr < max_calls)
    				{
    
    				double ergebnis;
    				ergebnis = return_of_spl_string(_str);
    
    				SRF_RETURN_NEXT(funcctx,ergebnis);
    				}
    				//When he finish the last row the function is done
    					if(call_cntr = max_calls)
    						{
    						SRF_RETURN_DONE(funcctx);
    						}
    }
    

    Er baut auch brav, die Funktion lässt sich auch mit CREATE ... einfügen bloß scheitert es an der Abfrage der Daten.

    Als Fehlermeldung kriege ich dann

    FEHLER:  Funktion mit Mengenergebnis in einem Zusammenhang aufgerufen, der keine Mengenergebnisse verarbeiten kann
    
    ********** Fehler **********
    
    FEHLER: Funktion mit Mengenergebnis in einem Zusammenhang aufgerufen, der keine Mengenergebnisse verarbeiten kann
    SQL Status:0A000
    


  • Diesen ganzen Kram mit dem Kontext und so brauchst Du doch nicht, oder?
    Ich meine Du bekommst einen LINESTRING und gibst da die Norm zurück.

    Würde dementsprechend nicht reichen:

    PG_FUNCTION_INFO_V1(FCT_Norm);
    PGDLLEXPORT
    Datum
    FCT_Norm(PG_FUNCTION_ARGS)
    {
      // ©Wutz (https://www.c-plusplus.net/forum/329834)
      int n=0;
      double d;
      double sum=0.0;
      const char *s=strchr(PG_GETARG_CSTRING(0),'\'')+1;
      while( 1==sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) )
      {
        sum += d*d;
      }
      PG_RETURN_FLOAT8(sqrt(sum)); 
    }
    


  • Solange er mir durch die Spalte geht ist mir das recht egal 😃

    Danke für den Ansatz ich werde es morgen früh sofort versuchen (bin heute nicht mehr auf der Arbeit)

    🙂



  • Geht leider nicht bei dem Aufruf der Funktion in SQL schmiert mir die Verbindung komplett ab 😞 Also er bricht die Verbindung komplett ab



  • sataide schrieb:

    Geht leider nicht bei dem Aufruf der Funktion in SQL schmiert mir die Verbindung komplett ab 😞 Also er bricht die Verbindung komplett ab

    Ähm...ich glaube auch, dass s falsch initialisiert wird.

    Ein Linestring sieht so aus, oder:

    LINESTRING(0 0,1 1,1 2)

    ?

    Insbesondere fehlt das einfache Anführungszeichen ' .

    Also musst Du in strchr() nach einer öffnenden Klammer suchen '(', nicht nach '\''.



  • Ich trim den Linestring vor der Abfrage.

    Meine Abfrage sieht so aus :

    select FCT_Norm(trim(')' from trim('LINESTRING(' from st_AsText (coordinates)))) from germany.ttopology ;
    

    Als ausgabe gibts nur :

    ********** Fehler **********
    
    no connection to the server
    
    Verbindung zurückgesetzt.
    


  • Tohuwabohu hier...
    Wutz wollte den kompletten Linestring.

    Wenn Du in SQL trimst und dann ab der ersten Zahl übergibst, dann

    const char *s=PG_GETARG_CSTRING(0);
    


  • Hö? Wie meinste jetzt?



  • sataide schrieb:

    Hö? Wie meinste jetzt?

    Der const char *s im code wurde auf das erste Zeichen nach einem Anführungszeichen gesetzt.
    Weil der String so angenommen wurde:
    Linestring('0 0,1 1,1 2')

    Mittlerweile ist mir klar, dass der String so aussieht:
    Linestring(0 0,1 1,1 2)

    Und Du trimst weiterhin in SQL, dann kommt ja nur noch
    0 0,1 1,1 2
    an.
    D.h. s muss auch gar nicht angepasst werden und kann direkt auf PG_GETARG_CSTRING(0) gesetzt werden.

    PG_FUNCTION_INFO_V1(FCT_Norm);
    PGDLLEXPORT
    Datum
    FCT_Norm(PG_FUNCTION_ARGS)
    {
      // ©Wutz (https://www.c-plusplus.net/forum/329834)
      int n=0;
      double d;
      double sum=0.0;
      const char *s=PG_GETARG_CSTRING(0);
      while( 1==sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) )
      {
        sum += d*d;
      }
      PG_RETURN_FLOAT8(sqrt(sum));
    }
    


  • Wird direkt ausprobiert 🙂



  • Interessant.

    Bei der Abfrage :

    select FCT_Norm(trim(')' from trim('LINESTRING(' from st_AsText (coordinates)))) from germany_.ttopology LIMIT 100 ;
    

    Gibt er mir 100 Zeilen mit 0 aus.(In paar Ms)
    Geb ich aber 1000 ein hört er gar nicht mehr auf. Bin jetzt schon bei 3 Minuten.



  • Evtl. ist die Frage mittlerweile besser im DB Subforum aufgehoben.

    Ich selbst habe auch keine Erfahrung mit Postgresql.

    Kannst Du es nicht mal testen mit einer Kommandozeile o.ä.?

    SELECT FCT_Norm('0 0,1 1,1 2');
    


  • Da gibt er mir auch ne 0 aus.

    Wir wissen aber das er die C-Funktion benutzt, da er mir die Werte als double precission zurückgibt. Die Frage ist nur warum er mir eine 0 zurückgibt und keinen berechneten Wert.



  • Ich überfliege gerade dies: http://www.postgresql.org/docs/9.4/static/xfunc-c.html
    Demnach müssen wir das Ergebnis palloc() en und einen pointer darauf zurückgeben.

    Das musst Du nochmal genauer lesen.
    Insbesondere diesen Tipp: "Hence, the standard internal function library is a rich source of coding examples for user-defined C functions." Solltest Du berücksichtigen und Dir ein paar Beispiele anschauen.



  • Die Seite hab ich schon nebenbei auf 🙂

    Problem ist das meine Programmierkenntnisse noch ziemlich klein sind und mir diese Zeigergeschichte überhaupt nicht liegt.

    Ich versuchs mal und meld mich dann wieder 🙂



  • Btw.

    Muss ich den String auch pallocen oder nur das Ergebnis?



  • sataide schrieb:

    Muss ich den String auch pallocen oder nur das Ergebnis?

    Ich bin mir nicht einmal mehr sicher, ob das Ergebnis alloziert werden muss. 🙂

    Sorry: da musst Du jemanden fragen, der sich damit auskennt. Chancen sind im Datenbank-Subforum besser.



  • Ich hab den Fehler. Es war kein PG_GETARG_CSTRING sondern ein PG_GETARG_TEXT_P. Nach ein paar Änderungen im Code läuft es endlich einwandfrei und hat die Abfragezeit um mehr als die Hälfte verkürzt.
    Wollte mich nur für die Hilfe bedanken 🙂

    Wenns wieder Probleme gibt meld ich mich 😃



  • Eine Frage hätte ich noch?

    Wie kann ich diese Splitfunktion in eine eigene funktion aufteilen?

    Zum Beispiel

    void string_split(*hier kommt der String rein 😉

    und dann

    meine eigene Funktion, die diese Hilfsfunktion anwendet
    PG_Function...

    s*char string_split(*string*)

    ...


Anmelden zum Antworten