Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  prepare.c   Sprache: unbekannt

 
/* 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

[Verzeichnis aufwärts0.19unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-08]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=141584
#Domains=752002