Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Functions | Variables

prepare.c File Reference

#include "postgres_fe.h"
#include <ctype.h>
#include "ecpgtype.h"
#include "ecpglib.h"
#include "ecpgerrno.h"
#include "extern.h"
#include "sqlca.h"
Include dependency graph for prepare.c:

Go to the source code of this file.

Data Structures

struct  stmtCacheEntry

Defines

#define POSTGRES_ECPG_INTERNAL
#define STMTID_SIZE   32

Functions

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)
static bool replace_variables (char **text, int lineno)
static bool prepare_common (int lineno, struct connection *con, const char *name, const char *variable)
bool ECPGprepare (int lineno, const char *connection_name, const bool questionmarks, const char *name, const char *variable)
struct prepared_statementecpg_find_prepared_statement (const char *name, struct connection *con, struct prepared_statement **prev_)
bool ECPGdeallocate (int lineno, int c, const char *connection_name, const char *name)
bool ecpg_deallocate_all_conn (int lineno, enum COMPAT_MODE c, struct connection *con)
bool ECPGdeallocate_all (int lineno, int compat, const char *connection_name)
char * ecpg_prepared (const char *name, struct connection *con)
char * ECPGprepared_statement (const char *connection_name, const char *name, int lineno)
static int HashStmt (const char *ecpgQuery)
static int SearchStmtCache (const char *ecpgQuery)
static int ecpg_freeStmtCacheEntry (int lineno, int compat, int entNo)
static int AddStmtToCache (int lineno, const char *stmtID, const char *connection, int compat, const char *ecpgQuery)
bool ecpg_auto_prepare (int lineno, const char *connection_name, const int compat, char **name, const char *query)

Variables

static int nextStmtID = 1
static const int stmtCacheNBuckets = 2039
static const int stmtCacheEntPerBucket = 8
static stmtCacheEntry stmtCacheEntries [16384] = {{0, {0}, 0, 0, 0}}

Define Documentation

#define POSTGRES_ECPG_INTERNAL

Definition at line 3 of file prepare.c.

#define STMTID_SIZE   32

Definition at line 14 of file prepare.c.


Function Documentation

static int AddStmtToCache ( int  lineno,
const char *  stmtID,
const char *  connection,
int  compat,
const char *  ecpgQuery 
) [static]

Definition at line 418 of file prepare.c.

References stmtCacheEntry::connection, ecpg_freeStmtCacheEntry(), ecpg_strdup(), stmtCacheEntry::ecpgQuery, stmtCacheEntry::execs, HashStmt(), stmtCacheEntry::lineno, stmtCacheEntPerBucket, and stmtCacheEntry::stmtID.

Referenced by ecpg_auto_prepare().

{
    int         ix,
                initEntNo,
                luEntNo,
                entNo;
    stmtCacheEntry *entry;

/* 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 - use the 'least used' entry found in the bucket     */
    if (ix >= stmtCacheEntPerBucket)    /* if no unused entries were found  */
        entNo = luEntNo;        /* re-use the 'least used' entry    */

/* '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);
}

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

Definition at line 201 of file prepare.c.

References statement::command, connection::connection, statement::connection, ecpg_alloc(), ecpg_check_PQresult(), ecpg_free(), ECPG_INVALID_STMT, ecpg_log(), ecpg_raise(), ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, INFORMIX_MODE, statement::lineno, name, prepared_statement::next, NULL, PQclear(), PQexec(), connection::prep_stmts, prepared_statement::prepared, and prepared_statement::stmt.

Referenced by ecpg_deallocate_all_conn(), ecpg_freeStmtCacheEntry(), ECPGdeallocate(), and ECPGprepare().

{
    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;
}

bool ecpg_auto_prepare ( int  lineno,
const char *  connection_name,
const int  compat,
char **  name,
const char *  query 
)

Definition at line 468 of file prepare.c.

References AddStmtToCache(), ecpg_find_prepared_statement(), ecpg_get_connection(), ecpg_log(), ecpg_strdup(), ECPGprepare(), stmtCacheEntry::execs, nextStmtID, NULL, prepare_common(), SearchStmtCache(), and stmtCacheEntry::stmtID.

Referenced by ECPGdo().

{
    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);
        if (AddStmtToCache(lineno, stmtID, connection_name, compat, query) < 0)
            return (false);

        *name = ecpg_strdup(stmtID, lineno);
    }

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

    return (true);
}

bool ecpg_deallocate_all_conn ( int  lineno,
enum COMPAT_MODE  c,
struct connection con 
)

Definition at line 276 of file prepare.c.

References deallocate_one(), NULL, and connection::prep_stmts.

Referenced by ecpg_finish(), and ECPGdeallocate_all().

{
    /* deallocate all prepared statements */
    while (con->prep_stmts)
    {
        if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
            return false;
    }

    return true;
}

struct prepared_statement* ecpg_find_prepared_statement ( const char *  name,
struct connection con,
struct prepared_statement **  prev_ 
) [read]

Definition at line 182 of file prepare.c.

References prepared_statement::next, NULL, and connection::prep_stmts.

Referenced by ecpg_auto_prepare(), ecpg_freeStmtCacheEntry(), ecpg_prepared(), ECPGdeallocate(), ECPGdescribe(), and ECPGprepare().

{
    struct prepared_statement *this,
               *prev;

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

static int ecpg_freeStmtCacheEntry ( int  lineno,
int  compat,
int  entNo 
) [static]

Definition at line 383 of file prepare.c.

References stmtCacheEntry::connection, deallocate_one(), ecpg_find_prepared_statement(), ecpg_free(), ecpg_get_connection(), stmtCacheEntry::ecpgQuery, and stmtCacheEntry::stmtID.

Referenced by AddStmtToCache().

{
    stmtCacheEntry *entry;
    struct connection *con;
    struct prepared_statement *this,
               *prev;

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

    con = ecpg_get_connection(entry->connection);

    /* 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);
}

char* ecpg_prepared ( const char *  name,
struct connection con 
)

Definition at line 295 of file prepare.c.

References statement::command, ecpg_find_prepared_statement(), NULL, and prepared_statement::stmt.

Referenced by ECPGdo(), and ECPGprepared_statement().

{
    struct prepared_statement *this;

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

bool ECPGdeallocate ( int  lineno,
int  c,
const char *  connection_name,
const char *  name 
)

Definition at line 253 of file prepare.c.

References deallocate_one(), ecpg_find_prepared_statement(), ecpg_get_connection(), ecpg_init(), ECPG_INVALID_STMT, ecpg_raise(), ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, and INFORMIX_MODE.

Referenced by main().

{
    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 ECPGdeallocate_all ( int  lineno,
int  compat,
const char *  connection_name 
)

Definition at line 289 of file prepare.c.

References ecpg_deallocate_all_conn(), and ecpg_get_connection().

Referenced by main().

{
    return ecpg_deallocate_all_conn(lineno, compat, ecpg_get_connection(connection_name));
}

bool ECPGprepare ( int  lineno,
const char *  connection_name,
const bool  questionmarks,
const char *  name,
const char *  variable 
)

Definition at line 161 of file prepare.c.

References deallocate_one(), ECPG_COMPAT_PGSQL, ecpg_find_prepared_statement(), ecpg_get_connection(), ecpg_init(), and prepare_common().

Referenced by ecpg_auto_prepare(), main(), and test().

{
    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);
}

char* ECPGprepared_statement ( const char *  connection_name,
const char *  name,
int  lineno 
)

Definition at line 306 of file prepare.c.

References ecpg_get_connection(), and ecpg_prepared().

Referenced by main(), and test().

{
    (void) lineno;              /* keep the compiler quiet */
    return ecpg_prepared(name, ecpg_get_connection(connection_name));
}

static int HashStmt ( const char *  ecpgQuery  )  [static]

Definition at line 316 of file prepare.c.

References stmtCacheEntPerBucket, and stmtCacheNBuckets.

Referenced by AddStmtToCache(), and SearchStmtCache().

{
    int         stmtIx,
                bucketNo,
                hashLeng,
                stmtLeng;
    long long   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 + (int) ecpgQuery[stmtIx];
        hashVal = hashVal << 13;
        rotVal = (hashVal & 0x1fff00000000LL) >> 32;
        hashVal = (hashVal & 0xffffffffLL) | rotVal;
    }

    bucketNo = hashVal % stmtCacheNBuckets;
    bucketNo += 1;              /* don't use bucket # 0         */

    return (bucketNo * stmtCacheEntPerBucket);
}

static bool isvarchar ( unsigned char  c  )  [static]

Definition at line 34 of file prepare.c.

Referenced by replace_variables().

{
    if (isalnum(c))
        return true;

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

    if (c >= 128)
        return true;

    return (false);
}

static bool prepare_common ( int  lineno,
struct connection con,
const char *  name,
const char *  variable 
) [static]

Definition at line 103 of file prepare.c.

References statement::command, statement::compat, connection::connection, statement::connection, ecpg_alloc(), ecpg_check_PQresult(), ecpg_free(), ecpg_log(), ecpg_strdup(), statement::inlist, statement::lineno, next(), NULL, statement::outlist, PQclear(), PQprepare(), connection::prep_stmts, and replace_variables().

Referenced by ecpg_auto_prepare(), and ECPGprepare().

{
    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;
}

static bool replace_variables ( char **  text,
int  lineno 
) [static]

Definition at line 49 of file prepare.c.

References ecpg_alloc(), ecpg_free(), isvarchar(), and snprintf().

Referenced by prepare_common().

{
    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
        {
            int         len;
            int         buffersize = sizeof(int) * CHAR_BIT * 10 / 3;   /* a rough guess of the
                                                                         * size we need */
            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++);
            if (!(newcopy = (char *) ecpg_alloc(strlen(*text) -len + strlen(buffer) + 1, lineno)))
            {
                ecpg_free(buffer);
                return false;
            }

            strncpy(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 int SearchStmtCache ( const char *  ecpgQuery  )  [static]

Definition at line 351 of file prepare.c.

References HashStmt(), and stmtCacheEntPerBucket.

Referenced by ecpg_auto_prepare().

{
    int         entNo,
                entIx;

/* 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);
}


Variable Documentation

int nextStmtID = 1 [static]

Definition at line 25 of file prepare.c.

Referenced by ecpg_auto_prepare().

const int stmtCacheEntPerBucket = 8 [static]

Definition at line 27 of file prepare.c.

Referenced by AddStmtToCache(), HashStmt(), and SearchStmtCache().

stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}} [static]

Definition at line 28 of file prepare.c.

const int stmtCacheNBuckets = 2039 [static]

Definition at line 26 of file prepare.c.

Referenced by HashStmt().