PostgreSQL Externe C-Funktion



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



  • Wutz schrieb:

    Den gesamten String in C verarbeiten:

    int main(void)
    {
      const char *s="LINESTRING('3.584731 60.739211,3.590472 60.738030,3.592740 60.736220')";
      int n=0;
      double d;
      s=strchr(s,'\'')+1;
      while( 1==sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) )
        printf("\n%f",d);
      return 0;
    }
    

    http://ideone.com/f9U1KJ

    Wow vielen Dank. Damit komm ich meinem Ziel schon bedeutend näher. Wenn du mir noch zeigen würdest wie ich mit dem Ergebnis weiterarbeiten kann wäre ich echt sehr dankbar. Bzw könnte ich in der WHile-Schleife die ausgegeben Werte summieren und quadrieren ?



  • Erweitere Zeile 9 nach deinem belieben, auch als Block. In d ist der aktuelle Wert gespeichert.



  • Packe den ganzen Kram in eine Funktion, übergib den const char* und arbeite innerhalb von while die Werte ab und überlege dir, was die Funktion evtl. rückliefern soll(Final-Ergebnis...).
    Sowas nennt man auch Programmdesign.



  • Sorry wenn ich doof frage aber warum const char?
    Nach meiner Denkweise ist es in jeder Zeile ein anderer String und somit doch kein const char. Aber belehrt mich eines besseren, bin auch nur ein Neuling und bereit was zu lernen 🙂

    @Wutz
    Meinste so:

    double return_of_spl_string(const char*s)
    {
      int n=0;
      double d;
      s=strchr(s,'\'')+1;
      while( 1==sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) )
    //...hier einfach pow,sum,sqrt anwenden ...
      return d;
    }
    


  • sataide schrieb:

    Sorry wenn ich doof frage aber warum const char?
    Nach meiner Denkweise ist es in jeder Zeile ein anderer String und somit doch kein const char. Aber belehrt mich eines besseren, bin auch nur ein Neuling und bereit was zu lernen 🙂

    const char*
    Du veränderst den String ja nicht in der Funktion. In der bleibt er const.

    sataide schrieb:

    Meinste so:

    double return_of_spl_string(const char*s)
    {
      int n=0;
      double d;
      s=strchr(s,'\'')+1;
      while( 1==sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) )
    //...hier einfach pow,sum,sqrt anwenden ...
      return d;
    }
    

    Das wird etwas schwieriger, da du ja jeden einzelnen Wert abspeichern willst.
    Aus der Funktion bekommst du aber nur das Ergebnis der ganzen Zeile.
    Oder du musst noch die aktuelle Summe mit übergeben und die neue wieder rausgeben.



  • const char* heißt, dass der übergebene String innerhalb der C Funktion nicht verändert werden darf, aber natürlich beim nächsten Funktionsaufruf durchaus ein anderer String sein darf. const-Design gehört auch zum Programmdesign und zwingt dazu, sich über die ausgetauschten Daten im Klaren zu sein.



  • @Dirk
    Meinste so?

    double return_of_spl_string(const char*s)
    {
      int n=0;
      double d;
      double sum;
      s=strchr(s,'\'')+1;
      while(sscanf(s+=n,"%lf%*1[ ,]%n",&d,&n) ) 
      {
    	  sum += pow(d, 2);  
      }
    
      return pow(sum, 0.5);
    }
    

    Diese Funktion sollte mir ein quadrierten und in eine Wurzel gepackten Wert zurückgeben.



  • 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.


Anmelden zum Antworten