Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  prepare.c

  Sprache: C
 

/* src/interfaces/ecpg/ecpglib/prepare.c */

#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"

#include <ctype.h>

#include "ecpgerrno.h"
#include "ecpglib.h"
#include "ecpglib_extern.h"
#include "ecpgtype.h"
#include "sqlca.h"

#define STMTID_SIZE 32

/*
 * The statement cache contains stmtCacheNBuckets hash buckets, each
 * having stmtCacheEntPerBucket entries, which we recycle as needed,
 * giving up the least-executed entry in the bucket.
 * stmtCacheEntries[0] is never used, so that zero can be a "not found"
 * indicator.
 */

#define stmtCacheNBuckets  2039 /* should be a prime number */
#define stmtCacheEntPerBucket 8

#define stmtCacheArraySize (stmtCacheNBuckets * stmtCacheEntPerBucket + 1)

typedef struct
{
 int   lineno;
 char  stmtID[STMTID_SIZE];
 char    *ecpgQuery;
 long  execs;   /* # of executions */
 const char *connection;  /* connection for the statement */
} stmtCacheEntry;

static int nextStmtID = 1;
static stmtCacheEntry *stmtCacheEntries = NULL;

static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
         struct prepared_statement *prev, struct prepared_statement *this);

static bool
isvarchar(unsigned char c)
{
 if (isalnum(c))
  return true;

 if (c == '_' || c == '>' || c == '-' || c == '.')
  return true;

 if (c >= 128)
  return true;

 return false;
}

bool
ecpg_register_prepared_stmt(struct statement *stmt)
{
 struct statement *prep_stmt;
 struct prepared_statement *this;
 struct connection *con = stmt->connection;
 struct prepared_statement *prev = NULL;
 int   lineno = stmt->lineno;

 /* check if we already have prepared this statement */
 this = ecpg_find_prepared_statement(stmt->name, con, &prev);
 if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
  return false;

 /* allocate new statement */
 this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
 if (!this)
  return false;

 prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
 if (!prep_stmt)
 {
  ecpg_free(this);
  return false;
 }
 memset(prep_stmt, 0sizeof(struct statement));

 /* create statement */
 prep_stmt->lineno = lineno;
 prep_stmt->connection = con;
 prep_stmt->command = ecpg_strdup(stmt->command, lineno);
 prep_stmt->inlist = prep_stmt->outlist = NULL;
 this->name = ecpg_strdup(stmt->name, lineno);
 this->stmt = prep_stmt;
 this->prepared = true;

 if (con->prep_stmts == NULL)
  this->next = NULL;
 else
  this->next = con->prep_stmts;

 con->prep_stmts = this;
 return true;
}

static bool
replace_variables(char **text, int lineno)
{
 bool  string = false;
 int   counter = 1,
    ptr = 0;

 for (; (*text)[ptr] != '\0'; ptr++)
 {
  if ((*text)[ptr] == '\'')
   string = string ? false : true;

  if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
   continue;

  if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
   ptr += 2;   /* skip  '::' */
  else
  {
   /* a rough guess of the size we need: */
   int   buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
   int   len;
   char    *buffer,
        *newcopy;

   if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
    return false;

   snprintf(buffer, buffersize, "$%d", counter++);

   for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++)
     /* skip */ ;
   if (!(newcopy = (char *) ecpg_alloc(strlen(*text) - len + strlen(buffer) + 1, lineno)))
   {
    ecpg_free(buffer);
    return false;
   }

   memcpy(newcopy, *text, ptr);
   strcpy(newcopy + ptr, buffer);
   strcat(newcopy, (*text) +ptr + len);

   ecpg_free(*text);
   ecpg_free(buffer);

   *text = newcopy;

   if ((*text)[ptr] == '\0'/* we reached the end */
    ptr--;   /* since we will (*text)[ptr]++ in the top
 * level for loop */

  }
 }
 return true;
}

static bool
prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
{
 struct statement *stmt;
 struct prepared_statement *this;
 PGresult   *query;

 /* allocate new statement */
 this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
 if (!this)
  return false;

 stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
 if (!stmt)
 {
  ecpg_free(this);
  return false;
 }

 /* create statement */
 stmt->lineno = lineno;
 stmt->connection = con;
 stmt->command = ecpg_strdup(variable, lineno);
 stmt->inlist = stmt->outlist = NULL;

 /* if we have C variables in our statement replace them with '?' */
 replace_variables(&(stmt->command), lineno);

 /* add prepared statement to our list */
 this->name = ecpg_strdup(name, lineno);
 this->stmt = stmt;

 /* and finally really prepare the statement */
 query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
 if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
 {
  ecpg_free(stmt->command);
  ecpg_free(this->name);
  ecpg_free(this);
  ecpg_free(stmt);
  return false;
 }

 ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
 PQclear(query);
 this->prepared = true;

 if (con->prep_stmts == NULL)
  this->next = NULL;
 else
  this->next = con->prep_stmts;

 con->prep_stmts = this;
 return true;
}

/* handle the EXEC SQL PREPARE statement */
/* questionmarks is not needed but remains in there for the time being to not change the API */
bool
ECPGprepare(int lineno, const char *connection_name, const bool questionmarks,
   const char *name, const char *variable)
{
 struct connection *con;
 struct prepared_statement *this,
      *prev;

 (void) questionmarks;  /* quiet the compiler */

 con = ecpg_get_connection(connection_name);
 if (!ecpg_init(con, connection_name, lineno))
  return false;

 /* check if we already have prepared this statement */
 this = ecpg_find_prepared_statement(name, con, &prev);
 if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
  return false;

 return prepare_common(lineno, con, name, variable);
}

struct prepared_statement *
ecpg_find_prepared_statement(const char *name,
        struct connection *con, struct prepared_statement **prev_)
{
 struct prepared_statement *this,
      *prev;

 for (this = con->prep_stmts, prev = NULL;
   this != NULL;
   prev = thisthis = this->next)
 {
  if (strcmp(this->name, name) == 0)
  {
   if (prev_)
    *prev_ = prev;
   return this;
  }
 }
 return NULL;
}

static bool
deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
      struct prepared_statement *prev, struct prepared_statement *this)
{
 bool  r = false;

 ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);

 /* first deallocate the statement in the backend */
 if (this->prepared)
 {
  char    *text;
  PGresult   *query;

  text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);

  if (text)
  {
   sprintf(text, "deallocate \"%s\""this->name);
   query = PQexec(this->stmt->connection->connection, text);
   ecpg_free(text);
   if (ecpg_check_PQresult(query, lineno,
         this->stmt->connection->connection,
         this->stmt->compat))
   {
    PQclear(query);
    r = true;
   }
  }
 }

 /*
  * Just ignore all errors since we do not know the list of cursors we are
  * allowed to free. We have to trust the software.
 */

 if (!r && !INFORMIX_MODE(c))
 {
  ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
  return false;
 }

 /* okay, free all the resources */
 ecpg_free(this->stmt->command);
 ecpg_free(this->stmt);
 ecpg_free(this->name);
 if (prev != NULL)
  prev->next = this->next;
 else
  con->prep_stmts = this->next;

 ecpg_free(this);
 return true;
}

/* handle the EXEC SQL DEALLOCATE PREPARE statement */
bool
ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
{
 struct connection *con;
 struct prepared_statement *this,
      *prev;

 con = ecpg_get_connection(connection_name);
 if (!ecpg_init(con, connection_name, lineno))
  return false;

 this = ecpg_find_prepared_statement(name, con, &prev);
 if (this)
  return deallocate_one(lineno, c, con, prev, this);

 /* prepared statement is not found */
 if (INFORMIX_MODE(c))
  return true;
 ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
 return false;
}

bool
ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
{
 /* deallocate all prepared statements */
 while (con->prep_stmts)
 {
  if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
   return false;
 }

 return true;
}

bool
ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
{
 struct connection *con = ecpg_get_connection(connection_name);

 if (!ecpg_init(con, connection_name, lineno))
  return false;

 return ecpg_deallocate_all_conn(lineno, compat, con);
}

char *
ecpg_prepared(const char *name, struct connection *con)
{
 struct prepared_statement *this;

 this = ecpg_find_prepared_statement(name, con, NULL);
 return this ? this->stmt->command : NULL;
}

/* return the prepared statement */
char *
ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
{
 struct connection *con = ecpg_get_connection(connection_name);

 if (!ecpg_init(con, connection_name, lineno))
  return NULL;

 return ecpg_prepared(name, con);
}

/*
 * hash a SQL statement -  returns entry # of first entry in the bucket
 */

static int
HashStmt(const char *ecpgQuery)
{
 int   stmtIx,
    bucketNo,
    hashLeng,
    stmtLeng;
 uint64  hashVal,
    rotVal;

 stmtLeng = strlen(ecpgQuery);
 hashLeng = 50;    /* use 1st 50 characters of statement */
 if (hashLeng > stmtLeng) /* if the statement isn't that long */
  hashLeng = stmtLeng; /* use its actual length */

 hashVal = 0;
 for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
 {
  hashVal = hashVal + (unsigned char) ecpgQuery[stmtIx];
  /* rotate 32-bit hash value left 13 bits */
  hashVal = hashVal << 13;
  rotVal = (hashVal & UINT64CONST(0x1fff00000000)) >> 32;
  hashVal = (hashVal & UINT64CONST(0xffffffff)) | rotVal;
 }

 bucketNo = hashVal % stmtCacheNBuckets;

 /* Add 1 so that array entry 0 is never used */
 return bucketNo * stmtCacheEntPerBucket + 1;
}

/*
 * search the statement cache - search for entry with matching ECPG-format query
 * Returns entry # in cache if found
 *  OR  zero if not present (zero'th entry isn't used)
 */

static int
SearchStmtCache(const char *ecpgQuery)
{
 int   entNo,
    entIx;

 /* quick failure if cache not set up */
 if (stmtCacheEntries == NULL)
  return 0;

 /* hash the statement */
 entNo = HashStmt(ecpgQuery);

 /* search the cache */
 for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
 {
  if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */
  {
   if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
    break;   /* found it */
  }
  ++entNo;    /* incr entry # */
 }

 /* if entry wasn't found - set entry # to zero */
 if (entIx >= stmtCacheEntPerBucket)
  entNo = 0;

 return entNo;
}

/*
 * free an entry in the statement cache
 * Returns entry # in cache used
 *  OR  negative error code
 */

static int
ecpg_freeStmtCacheEntry(int lineno, int compat,
      int entNo) /* entry # to free */
{
 stmtCacheEntry *entry;
 struct connection *con;
 struct prepared_statement *this,
      *prev;

 /* fail if cache isn't set up */
 if (stmtCacheEntries == NULL)
  return -1;

 entry = &stmtCacheEntries[entNo];
 if (!entry->stmtID[0])  /* return if the entry isn't in use */
  return 0;

 con = ecpg_get_connection(entry->connection);

 /*
  * If the connection is gone, the prepared_statement list it owned is
  * already unreachable, so just skip that cleanup.  We must still clear
  * the cache slot below so it can be reused.
 */

 if (con)
 {
  /* free the 'prepared_statement' list entry */
  this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
  if (this && !deallocate_one(lineno, compat, con, prev, this))
   return -1;
 }

 entry->stmtID[0] = '\0';

 /* free the memory used by the cache entry */
 if (entry->ecpgQuery)
 {
  ecpg_free(entry->ecpgQuery);
  entry->ecpgQuery = 0;
 }

 return entNo;
}

/*
 * add an entry to the statement cache
 * returns entry # in cache used  OR  negative error code
 */

static int
AddStmtToCache(int lineno,  /* line # of statement */
      const char *stmtID, /* statement ID */
      const char *connection, /* connection */
      int compat,  /* compatibility level */
      const char *ecpgQuery) /* query */
{
 int   ix,
    initEntNo,
    luEntNo,
    entNo;
 stmtCacheEntry *entry;

 /* allocate and zero cache array if we haven't already */
 if (stmtCacheEntries == NULL)
 {
  stmtCacheEntries = (stmtCacheEntry *)
   ecpg_alloc(sizeof(stmtCacheEntry) * stmtCacheArraySize, lineno);
  if (stmtCacheEntries == NULL)
   return -1;
 }

 /* hash the statement */
 initEntNo = HashStmt(ecpgQuery);

 /* search for an unused entry */
 entNo = initEntNo;   /* start with the initial entry # for the
 * bucket */

 luEntNo = initEntNo;  /* use it as the initial 'least used' entry */
 for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
 {
  entry = &stmtCacheEntries[entNo];
  if (!entry->stmtID[0]) /* unused entry  - use it */
   break;
  if (entry->execs < stmtCacheEntries[luEntNo].execs)
   luEntNo = entNo; /* save new 'least used' entry */
  ++entNo;    /* increment entry # */
 }

 /*
  * if no unused entries were found, re-use the 'least used' entry found in
  * the bucket
 */

 if (ix >= stmtCacheEntPerBucket)
  entNo = luEntNo;

 /* 'entNo' is the entry to use - make sure its free */
 if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
  return -1;

 /* add the query to the entry */
 entry = &stmtCacheEntries[entNo];
 entry->lineno = lineno;
 entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
 entry->connection = connection;
 entry->execs = 0;
 memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));

 return entNo;
}

/* handle cache and preparation of statements in auto-prepare mode */
bool
ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
{
 int   entNo;

 /* search the statement cache for this statement */
 entNo = SearchStmtCache(query);

 /* if not found - add the statement to the cache */
 if (entNo)
 {
  char    *stmtID;
  struct connection *con;
  struct prepared_statement *prep;

  ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);

  stmtID = stmtCacheEntries[entNo].stmtID;

  con = ecpg_get_connection(connection_name);
  prep = ecpg_find_prepared_statement(stmtID, con, NULL);
  /* This prepared name doesn't exist on this connection. */
  if (!prep && !prepare_common(lineno, con, stmtID, query))
   return false;

  *name = ecpg_strdup(stmtID, lineno);
 }
 else
 {
  char  stmtID[STMTID_SIZE];

  ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);

  /* generate a statement ID */
  sprintf(stmtID, "ecpg%d", nextStmtID++);

  if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
   return false;

  entNo = AddStmtToCache(lineno, stmtID, connection_name, compat, query);
  if (entNo < 0)
   return false;

  *name = ecpg_strdup(stmtID, lineno);
 }

 /* increase usage counter */
 stmtCacheEntries[entNo].execs++;

 return true;
}

Messung V0.5 in Prozent
C=89 H=97 G=93

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002