LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/ecpglib - prepare.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 279 339 82.3 %
Date: 2019-08-24 16:07:17 Functions: 23 23 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* src/interfaces/ecpg/ecpglib/prepare.c */
       2             : 
       3             : #define POSTGRES_ECPG_INTERNAL
       4             : #include "postgres_fe.h"
       5             : 
       6             : #include <ctype.h>
       7             : 
       8             : #include "ecpgtype.h"
       9             : #include "ecpglib.h"
      10             : #include "ecpgerrno.h"
      11             : #include "ecpglib_extern.h"
      12             : #include "sqlca.h"
      13             : 
      14             : #define STMTID_SIZE 32
      15             : 
      16             : /*
      17             :  * The statement cache contains stmtCacheNBuckets hash buckets, each
      18             :  * having stmtCacheEntPerBucket entries, which we recycle as needed,
      19             :  * giving up the least-executed entry in the bucket.
      20             :  * stmtCacheEntries[0] is never used, so that zero can be a "not found"
      21             :  * indicator.
      22             :  */
      23             : #define stmtCacheNBuckets       2039    /* should be a prime number */
      24             : #define stmtCacheEntPerBucket   8
      25             : 
      26             : #define stmtCacheArraySize (stmtCacheNBuckets * stmtCacheEntPerBucket + 1)
      27             : 
      28             : typedef struct
      29             : {
      30             :     int         lineno;
      31             :     char        stmtID[STMTID_SIZE];
      32             :     char       *ecpgQuery;
      33             :     long        execs;          /* # of executions */
      34             :     const char *connection;     /* connection for the statement */
      35             : } stmtCacheEntry;
      36             : 
      37             : static int  nextStmtID = 1;
      38             : static stmtCacheEntry *stmtCacheEntries = NULL;
      39             : static struct declared_statement *g_declared_list;
      40             : 
      41             : static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
      42             :                            struct prepared_statement *prev, struct prepared_statement *this);
      43             : static struct declared_statement *ecpg_find_declared_statement(const char *);
      44             : static bool
      45        3308 : isvarchar(unsigned char c)
      46             : {
      47        3308 :     if (isalnum(c))
      48          12 :         return true;
      49             : 
      50        3296 :     if (c == '_' || c == '>' || c == '-' || c == '.')
      51           0 :         return true;
      52             : 
      53        3296 :     if (c >= 128)
      54           0 :         return true;
      55             : 
      56        3296 :     return false;
      57             : }
      58             : 
      59             : bool
      60          20 : ecpg_register_prepared_stmt(struct statement *stmt)
      61             : {
      62             :     struct statement *prep_stmt;
      63             :     struct prepared_statement *this;
      64          20 :     struct connection *con = NULL;
      65          20 :     struct prepared_statement *prev = NULL;
      66             :     char       *real_connection_name;
      67          20 :     int         lineno = stmt->lineno;
      68             : 
      69          20 :     real_connection_name = ecpg_get_con_name_by_declared_name(stmt->name);
      70          20 :     if (real_connection_name == NULL)
      71          20 :         real_connection_name = stmt->connection->name;
      72             : 
      73          20 :     con = ecpg_get_connection(real_connection_name);
      74          20 :     if (!ecpg_init(con, real_connection_name, stmt->lineno))
      75           0 :         return false;
      76             : 
      77             :     /* check if we already have prepared this statement */
      78          20 :     this = ecpg_find_prepared_statement(stmt->name, con, &prev);
      79          20 :     if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
      80           0 :         return false;
      81             : 
      82             :     /* allocate new statement */
      83          20 :     this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
      84          20 :     if (!this)
      85           0 :         return false;
      86             : 
      87          20 :     prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
      88          20 :     if (!prep_stmt)
      89             :     {
      90           0 :         ecpg_free(this);
      91           0 :         return false;
      92             :     }
      93          20 :     memset(prep_stmt, 0, sizeof(struct statement));
      94             : 
      95             :     /* create statement */
      96          20 :     prep_stmt->lineno = lineno;
      97          20 :     prep_stmt->connection = con;
      98          20 :     prep_stmt->command = ecpg_strdup(stmt->command, lineno);
      99          20 :     prep_stmt->inlist = prep_stmt->outlist = NULL;
     100          20 :     this->name = ecpg_strdup(stmt->name, lineno);
     101          20 :     this->stmt = prep_stmt;
     102          20 :     this->prepared = true;
     103             : 
     104          20 :     if (con->prep_stmts == NULL)
     105           0 :         this->next = NULL;
     106             :     else
     107          20 :         this->next = con->prep_stmts;
     108             : 
     109          20 :     con->prep_stmts = this;
     110          20 :     return true;
     111             : }
     112             : 
     113             : static bool
     114        3416 : replace_variables(char **text, int lineno)
     115             : {
     116        3416 :     bool        string = false;
     117        3416 :     int         counter = 1,
     118        3416 :                 ptr = 0;
     119             : 
     120       96652 :     for (; (*text)[ptr] != '\0'; ptr++)
     121             :     {
     122       93236 :         if ((*text)[ptr] == '\'')
     123           0 :             string = string ? false : true;
     124             : 
     125       93236 :         if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
     126       89924 :             continue;
     127             : 
     128        3312 :         if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
     129           0 :             ptr += 2;           /* skip  '::' */
     130             :         else
     131             :         {
     132             :             /* a rough guess of the size we need: */
     133        3312 :             int         buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
     134             :             int         len;
     135             :             char       *buffer,
     136             :                        *newcopy;
     137             : 
     138        3312 :             if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
     139           0 :                 return false;
     140             : 
     141        3312 :             snprintf(buffer, buffersize, "$%d", counter++);
     142             : 
     143        3312 :             for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++)
     144             :                  /* skip */ ;
     145        3312 :             if (!(newcopy = (char *) ecpg_alloc(strlen(*text) -len + strlen(buffer) + 1, lineno)))
     146             :             {
     147           0 :                 ecpg_free(buffer);
     148           0 :                 return false;
     149             :             }
     150             : 
     151        3312 :             memcpy(newcopy, *text, ptr);
     152        3312 :             strcpy(newcopy + ptr, buffer);
     153        3312 :             strcat(newcopy, (*text) +ptr + len);
     154             : 
     155        3312 :             ecpg_free(*text);
     156        3312 :             ecpg_free(buffer);
     157             : 
     158        3312 :             *text = newcopy;
     159             : 
     160        3312 :             if ((*text)[ptr] == '\0')   /* we reached the end */
     161           0 :                 ptr--;          /* since we will (*text)[ptr]++ in the top
     162             :                                  * level for loop */
     163             :         }
     164             :     }
     165        3416 :     return true;
     166             : }
     167             : 
     168             : static bool
     169        3416 : prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
     170             : {
     171             :     struct statement *stmt;
     172             :     struct prepared_statement *this;
     173             :     PGresult   *query;
     174             : 
     175             :     /* allocate new statement */
     176        3416 :     this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
     177        3416 :     if (!this)
     178           0 :         return false;
     179             : 
     180        3416 :     stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
     181        3416 :     if (!stmt)
     182             :     {
     183           0 :         ecpg_free(this);
     184           0 :         return false;
     185             :     }
     186        3416 :     memset(stmt, 0, sizeof(struct statement));
     187             : 
     188             :     /* create statement */
     189        3416 :     stmt->lineno = lineno;
     190        3416 :     stmt->connection = con;
     191        3416 :     stmt->command = ecpg_strdup(variable, lineno);
     192        3416 :     stmt->inlist = stmt->outlist = NULL;
     193             : 
     194             :     /* if we have C variables in our statement replace them with '?' */
     195        3416 :     replace_variables(&(stmt->command), lineno);
     196             : 
     197             :     /* add prepared statement to our list */
     198        3416 :     this->name = ecpg_strdup(name, lineno);
     199        3416 :     this->stmt = stmt;
     200             : 
     201             :     /* and finally really prepare the statement */
     202        3416 :     query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
     203        3416 :     if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
     204             :     {
     205           0 :         ecpg_free(stmt->command);
     206           0 :         ecpg_free(this->name);
     207           0 :         ecpg_free(this);
     208           0 :         ecpg_free(stmt);
     209           0 :         return false;
     210             :     }
     211             : 
     212        3416 :     ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
     213        3416 :     PQclear(query);
     214        3416 :     this->prepared = true;
     215             : 
     216        3416 :     if (con->prep_stmts == NULL)
     217        3312 :         this->next = NULL;
     218             :     else
     219         104 :         this->next = con->prep_stmts;
     220             : 
     221        3416 :     con->prep_stmts = this;
     222        3416 :     return true;
     223             : }
     224             : 
     225             : /* handle the EXEC SQL PREPARE statement */
     226             : /* questionmarks is not needed but remains in there for the time being to not change the API */
     227             : bool
     228        3404 : ECPGprepare(int lineno, const char *connection_name, const bool questionmarks,
     229             :             const char *name, const char *variable)
     230             : {
     231             :     struct connection *con;
     232             :     struct prepared_statement *this,
     233             :                *prev;
     234        3404 :     const char *real_connection_name = NULL;
     235             : 
     236             :     (void) questionmarks;       /* quiet the compiler */
     237             : 
     238        3404 :     real_connection_name = ecpg_get_con_name_by_declared_name(name);
     239        3404 :     if (real_connection_name == NULL)
     240             :     {
     241             :         /*
     242             :          * If can't get the connection name by declared name then using
     243             :          * connection name coming from the parameter connection_name
     244             :          */
     245        3396 :         real_connection_name = connection_name;
     246             :     }
     247             : 
     248        3404 :     con = ecpg_get_connection(real_connection_name);
     249        3404 :     if (!ecpg_init(con, real_connection_name, lineno))
     250           0 :         return false;
     251             : 
     252             :     /* check if we already have prepared this statement */
     253        3404 :     this = ecpg_find_prepared_statement(name, con, &prev);
     254        3404 :     if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
     255           0 :         return false;
     256             : 
     257        3404 :     return prepare_common(lineno, con, name, variable);
     258             : }
     259             : 
     260             : struct prepared_statement *
     261        7140 : ecpg_find_prepared_statement(const char *name,
     262             :                              struct connection *con, struct prepared_statement **prev_)
     263             : {
     264             :     struct prepared_statement *this,
     265             :                *prev;
     266             : 
     267       14540 :     for (this = con->prep_stmts, prev = NULL;
     268             :          this != NULL;
     269         260 :          prev = this, this = this->next)
     270             :     {
     271        7112 :         if (strcmp(this->name, name) == 0)
     272             :         {
     273        6852 :             if (prev_)
     274        3356 :                 *prev_ = prev;
     275        6852 :             return this;
     276             :         }
     277             :     }
     278         288 :     return NULL;
     279             : }
     280             : 
     281             : static bool
     282        3428 : deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
     283             :                struct prepared_statement *prev, struct prepared_statement *this)
     284             : {
     285        3428 :     bool        r = false;
     286             : 
     287        3428 :     ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
     288             : 
     289             :     /* first deallocate the statement in the backend */
     290        3428 :     if (this->prepared)
     291             :     {
     292             :         char       *text;
     293             :         PGresult   *query;
     294             : 
     295        3428 :         text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
     296             : 
     297        3428 :         if (text)
     298             :         {
     299        3428 :             sprintf(text, "deallocate \"%s\"", this->name);
     300        3428 :             query = PQexec(this->stmt->connection->connection, text);
     301        3428 :             ecpg_free(text);
     302        6856 :             if (ecpg_check_PQresult(query, lineno,
     303        3428 :                                     this->stmt->connection->connection,
     304        3428 :                                     this->stmt->compat))
     305             :             {
     306        3428 :                 PQclear(query);
     307        3428 :                 r = true;
     308             :             }
     309             :         }
     310             :     }
     311             : 
     312             :     /*
     313             :      * Just ignore all errors since we do not know the list of cursors we are
     314             :      * allowed to free. We have to trust the software.
     315             :      */
     316        3428 :     if (!r && !INFORMIX_MODE(c))
     317             :     {
     318           0 :         ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
     319           0 :         return false;
     320             :     }
     321             : 
     322             :     /* okay, free all the resources */
     323        3428 :     ecpg_free(this->stmt->command);
     324        3428 :     ecpg_free(this->stmt);
     325        3428 :     ecpg_free(this->name);
     326        3428 :     if (prev != NULL)
     327          12 :         prev->next = this->next;
     328             :     else
     329        3416 :         con->prep_stmts = this->next;
     330             : 
     331        3428 :     ecpg_free(this);
     332        3428 :     return true;
     333             : }
     334             : 
     335             : /* handle the EXEC SQL DEALLOCATE PREPARE statement */
     336             : bool
     337         208 : ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
     338             : {
     339             :     struct connection *con;
     340             :     struct prepared_statement *this,
     341             :                *prev;
     342         208 :     const char *real_connection_name = NULL;
     343             : 
     344         208 :     real_connection_name = ecpg_get_con_name_by_declared_name(name);
     345         208 :     if (real_connection_name == NULL)
     346             :     {
     347             :         /*
     348             :          * If can't get the connection name by declared name then using
     349             :          * connection name coming from the parameter connection_name
     350             :          */
     351         200 :         real_connection_name = connection_name;
     352             :     }
     353             : 
     354         208 :     con = ecpg_get_connection(real_connection_name);
     355         208 :     if (!ecpg_init(con, real_connection_name, lineno))
     356           0 :         return false;
     357             : 
     358         208 :     this = ecpg_find_prepared_statement(name, con, &prev);
     359         208 :     if (this)
     360         208 :         return deallocate_one(lineno, c, con, prev, this);
     361             : 
     362             :     /* prepared statement is not found */
     363           0 :     if (INFORMIX_MODE(c))
     364           0 :         return true;
     365           0 :     ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
     366           0 :     return false;
     367             : }
     368             : 
     369             : bool
     370         512 : ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
     371             : {
     372             :     /* deallocate all prepared statements */
     373        1096 :     while (con->prep_stmts)
     374             :     {
     375          72 :         if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
     376           0 :             return false;
     377             :     }
     378             : 
     379         512 :     return true;
     380             : }
     381             : 
     382             : bool
     383           4 : ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
     384             : {
     385           4 :     return ecpg_deallocate_all_conn(lineno, compat,
     386             :                                     ecpg_get_connection(connection_name));
     387             : }
     388             : 
     389             : char *
     390        3408 : ecpg_prepared(const char *name, struct connection *con)
     391             : {
     392             :     struct prepared_statement *this;
     393             : 
     394        3408 :     this = ecpg_find_prepared_statement(name, con, NULL);
     395        3408 :     return this ? this->stmt->command : NULL;
     396             : }
     397             : 
     398             : /* return the prepared statement */
     399             : /* lineno is not used here, but kept in to not break API */
     400             : char *
     401          80 : ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
     402             : {
     403          80 :     const char *real_connection_name = NULL;
     404             : 
     405             :     (void) lineno;              /* keep the compiler quiet */
     406             : 
     407          80 :     real_connection_name = ecpg_get_con_name_by_declared_name(name);
     408          80 :     if (real_connection_name == NULL)
     409             :     {
     410             :         /*
     411             :          * If can't get the connection name by declared name then using
     412             :          * connection name coming from the parameter connection_name
     413             :          */
     414          72 :         real_connection_name = connection_name;
     415             :     }
     416             : 
     417          80 :     return ecpg_prepared(name, ecpg_get_connection(real_connection_name));
     418             : }
     419             : 
     420             : /*
     421             :  * hash a SQL statement -  returns entry # of first entry in the bucket
     422             :  */
     423             : static int
     424          40 : HashStmt(const char *ecpgQuery)
     425             : {
     426             :     int         stmtIx,
     427             :                 bucketNo,
     428             :                 hashLeng,
     429             :                 stmtLeng;
     430             :     uint64      hashVal,
     431             :                 rotVal;
     432             : 
     433          40 :     stmtLeng = strlen(ecpgQuery);
     434          40 :     hashLeng = 50;              /* use 1st 50 characters of statement */
     435          40 :     if (hashLeng > stmtLeng) /* if the statement isn't that long */
     436          40 :         hashLeng = stmtLeng;    /* use its actual length */
     437             : 
     438          40 :     hashVal = 0;
     439        1484 :     for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
     440             :     {
     441        1444 :         hashVal = hashVal + (unsigned char) ecpgQuery[stmtIx];
     442             :         /* rotate 32-bit hash value left 13 bits */
     443        1444 :         hashVal = hashVal << 13;
     444        1444 :         rotVal = (hashVal & UINT64CONST(0x1fff00000000)) >> 32;
     445        1444 :         hashVal = (hashVal & UINT64CONST(0xffffffff)) | rotVal;
     446             :     }
     447             : 
     448          40 :     bucketNo = hashVal % stmtCacheNBuckets;
     449             : 
     450             :     /* Add 1 so that array entry 0 is never used */
     451          40 :     return bucketNo * stmtCacheEntPerBucket + 1;
     452             : }
     453             : 
     454             : /*
     455             :  * search the statement cache - search for entry with matching ECPG-format query
     456             :  * Returns entry # in cache if found
     457             :  *   OR  zero if not present (zero'th entry isn't used)
     458             :  */
     459             : static int
     460          32 : SearchStmtCache(const char *ecpgQuery)
     461             : {
     462             :     int         entNo,
     463             :                 entIx;
     464             : 
     465             :     /* quick failure if cache not set up */
     466          32 :     if (stmtCacheEntries == NULL)
     467           4 :         return 0;
     468             : 
     469             :     /* hash the statement */
     470          28 :     entNo = HashStmt(ecpgQuery);
     471             : 
     472             :     /* search the cache */
     473          92 :     for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
     474             :     {
     475          84 :         if (stmtCacheEntries[entNo].stmtID[0])  /* check if entry is in use */
     476             :         {
     477          20 :             if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
     478          20 :                 break;          /* found it */
     479             :         }
     480          64 :         ++entNo;                /* incr entry # */
     481             :     }
     482             : 
     483             :     /* if entry wasn't found - set entry # to zero */
     484          28 :     if (entIx >= stmtCacheEntPerBucket)
     485           8 :         entNo = 0;
     486             : 
     487          28 :     return entNo;
     488             : }
     489             : 
     490             : /*
     491             :  * free an entry in the statement cache
     492             :  * Returns entry # in cache used
     493             :  *   OR  negative error code
     494             :  */
     495             : static int
     496          12 : ecpg_freeStmtCacheEntry(int lineno, int compat,
     497             :                         int entNo)  /* entry # to free */
     498             : {
     499             :     stmtCacheEntry *entry;
     500             :     struct connection *con;
     501             :     struct prepared_statement *this,
     502             :                *prev;
     503             : 
     504             :     /* fail if cache isn't set up */
     505          12 :     if (stmtCacheEntries == NULL)
     506           0 :         return -1;
     507             : 
     508          12 :     entry = &stmtCacheEntries[entNo];
     509          12 :     if (!entry->stmtID[0])       /* return if the entry isn't in use */
     510          12 :         return 0;
     511             : 
     512           0 :     con = ecpg_get_connection(entry->connection);
     513             : 
     514             :     /* free the 'prepared_statement' list entry */
     515           0 :     this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
     516           0 :     if (this && !deallocate_one(lineno, compat, con, prev, this))
     517           0 :         return -1;
     518             : 
     519           0 :     entry->stmtID[0] = '\0';
     520             : 
     521             :     /* free the memory used by the cache entry */
     522           0 :     if (entry->ecpgQuery)
     523             :     {
     524           0 :         ecpg_free(entry->ecpgQuery);
     525           0 :         entry->ecpgQuery = 0;
     526             :     }
     527             : 
     528           0 :     return entNo;
     529             : }
     530             : 
     531             : /*
     532             :  * add an entry to the statement cache
     533             :  * returns entry # in cache used  OR  negative error code
     534             :  */
     535             : static int
     536          12 : AddStmtToCache(int lineno,      /* line # of statement */
     537             :                const char *stmtID,  /* statement ID */
     538             :                const char *connection,  /* connection */
     539             :                int compat,      /* compatibility level */
     540             :                const char *ecpgQuery)   /* query */
     541             : {
     542             :     int         ix,
     543             :                 initEntNo,
     544             :                 luEntNo,
     545             :                 entNo;
     546             :     stmtCacheEntry *entry;
     547             : 
     548             :     /* allocate and zero cache array if we haven't already */
     549          12 :     if (stmtCacheEntries == NULL)
     550             :     {
     551           4 :         stmtCacheEntries = (stmtCacheEntry *)
     552           4 :             ecpg_alloc(sizeof(stmtCacheEntry) * stmtCacheArraySize, lineno);
     553           4 :         if (stmtCacheEntries == NULL)
     554           0 :             return -1;
     555             :     }
     556             : 
     557             :     /* hash the statement */
     558          12 :     initEntNo = HashStmt(ecpgQuery);
     559             : 
     560             :     /* search for an unused entry */
     561          12 :     entNo = initEntNo;          /* start with the initial entry # for the
     562             :                                  * bucket */
     563          12 :     luEntNo = initEntNo;        /* use it as the initial 'least used' entry */
     564          12 :     for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
     565             :     {
     566          12 :         entry = &stmtCacheEntries[entNo];
     567          12 :         if (!entry->stmtID[0])   /* unused entry  -  use it */
     568          12 :             break;
     569           0 :         if (entry->execs < stmtCacheEntries[luEntNo].execs)
     570           0 :             luEntNo = entNo;    /* save new 'least used' entry */
     571           0 :         ++entNo;                /* increment entry # */
     572             :     }
     573             : 
     574             :     /*
     575             :      * if no unused entries were found, re-use the 'least used' entry found in
     576             :      * the bucket
     577             :      */
     578          12 :     if (ix >= stmtCacheEntPerBucket)
     579           0 :         entNo = luEntNo;
     580             : 
     581             :     /* 'entNo' is the entry to use - make sure its free */
     582          12 :     if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
     583           0 :         return -1;
     584             : 
     585             :     /* add the query to the entry */
     586          12 :     entry = &stmtCacheEntries[entNo];
     587          12 :     entry->lineno = lineno;
     588          12 :     entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
     589          12 :     entry->connection = connection;
     590          12 :     entry->execs = 0;
     591          12 :     memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
     592             : 
     593          12 :     return entNo;
     594             : }
     595             : 
     596             : /* handle cache and preparation of statements in auto-prepare mode */
     597             : bool
     598          32 : ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
     599             : {
     600             :     int         entNo;
     601             : 
     602             :     /* search the statement cache for this statement */
     603          32 :     entNo = SearchStmtCache(query);
     604             : 
     605             :     /* if not found - add the statement to the cache */
     606          32 :     if (entNo)
     607             :     {
     608             :         char       *stmtID;
     609             :         struct connection *con;
     610             :         struct prepared_statement *prep;
     611             : 
     612          20 :         ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
     613             : 
     614          20 :         stmtID = stmtCacheEntries[entNo].stmtID;
     615             : 
     616          20 :         con = ecpg_get_connection(connection_name);
     617          20 :         prep = ecpg_find_prepared_statement(stmtID, con, NULL);
     618             :         /* This prepared name doesn't exist on this connection. */
     619          20 :         if (!prep && !prepare_common(lineno, con, stmtID, query))
     620           0 :             return false;
     621             : 
     622          20 :         *name = ecpg_strdup(stmtID, lineno);
     623             :     }
     624             :     else
     625             :     {
     626             :         char        stmtID[STMTID_SIZE];
     627             : 
     628          12 :         ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
     629             : 
     630             :         /* generate a statement ID */
     631          12 :         sprintf(stmtID, "ecpg%d", nextStmtID++);
     632             : 
     633          12 :         if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
     634           0 :             return false;
     635             : 
     636          12 :         entNo = AddStmtToCache(lineno, stmtID, connection_name, compat, query);
     637          12 :         if (entNo < 0)
     638           0 :             return false;
     639             : 
     640          12 :         *name = ecpg_strdup(stmtID, lineno);
     641             :     }
     642             : 
     643             :     /* increase usage counter */
     644          32 :     stmtCacheEntries[entNo].execs++;
     645             : 
     646          32 :     return true;
     647             : }
     648             : 
     649             : /*
     650             :  * handle the EXEC SQL DECLARE STATEMENT
     651             :  * Input: connection_name -- connection name
     652             :  *        name -- declared name
     653             :  */
     654             : bool
     655          20 : ECPGdeclare(int lineno, const char *connection_name, const char *name)
     656             : {
     657          20 :     struct connection *con = NULL;
     658          20 :     struct declared_statement *p = NULL;
     659             : 
     660          20 :     if (name == NULL)
     661             :     {
     662             :         /* Should never go to here because ECPG pre-compiler will check it */
     663           0 :         return false;
     664             :     }
     665             : 
     666          20 :     if (connection_name == NULL)
     667             :     {
     668             :         /*
     669             :          * Going to here means not using AT clause in the DECLARE STATEMENT
     670             :          * ECPG pre-processor allows this case. However, we don't allocate a
     671             :          * node to store the declared name because the DECLARE STATEMENT
     672             :          * without using AT clause will be ignored. The following statement
     673             :          * such as PREPARE, EXECUTE are executed as usual on the current
     674             :          * connection.
     675             :          */
     676          12 :         return true;
     677             :     }
     678             : 
     679           8 :     con = ecpg_get_connection(connection_name);
     680           8 :     if (!ecpg_init(con, connection_name, lineno))
     681           0 :         return false;
     682             : 
     683           8 :     if (ecpg_find_declared_statement(name))
     684             :     {
     685             :         /*
     686             :          * Should not go to here because the pre-compiler has check the
     687             :          * duplicate name
     688             :          */
     689           0 :         return false;
     690             :     }
     691             : 
     692             :     /* allocate a declared_statement as a new node */
     693           8 :     p = (struct declared_statement *) ecpg_alloc(sizeof(struct declared_statement), lineno);
     694           8 :     if (!p)
     695           0 :         return false;
     696             : 
     697           8 :     memset(p, 0, sizeof(struct declared_statement));
     698             : 
     699           8 :     ecpg_log("ECPGdeclare on line %d: declared name %s on connection: \"%s\"\n", lineno, name, connection_name);
     700             : 
     701           8 :     p->name = ecpg_strdup(name, lineno);
     702           8 :     p->connection_name = ecpg_strdup(connection_name, lineno);
     703             : 
     704             :     /* Add the new node into the g_declared_list */
     705           8 :     if (g_declared_list != NULL)
     706             :     {
     707           4 :         p->next = g_declared_list;
     708           4 :         g_declared_list = p;
     709             :     }
     710             :     else
     711           4 :         g_declared_list = p;
     712             : 
     713           8 :     return true;
     714             : }
     715             : 
     716             : /*
     717             :  * Find a declared node by declared name
     718             :  * Input: name -- declared name
     719             :  * Return: Found -- The pointer points to the declared node
     720             :  *         Not found -- NULL
     721             :  */
     722             : static struct declared_statement *
     723        7256 : ecpg_find_declared_statement(const char *name)
     724             : {
     725             :     struct declared_statement *p;
     726             : 
     727        7256 :     if (name == NULL)
     728          72 :         return NULL;
     729             : 
     730        7184 :     p = g_declared_list;
     731       14428 :     while (p)
     732             :     {
     733         100 :         if (strcmp(p->name, name) == 0)
     734          40 :             return p;
     735          60 :         p = p->next;
     736             :     }
     737             : 
     738        7144 :     return NULL;
     739             : }
     740             : 
     741             : /*
     742             :  * Build the relationship between the declared name and cursor name
     743             :  * Input: declared_name -- the name declared in the DECLARE STATEMENT
     744             :  *        cursor_name -- cursor name declared in the DECLARE/OPEN CURSOR statement
     745             :  */
     746             : void
     747           8 : ecpg_update_declare_statement(const char *declared_name, const char *cursor_name, const int lineno)
     748             : {
     749           8 :     struct declared_statement *p = NULL;
     750             : 
     751           8 :     if (!declared_name || !cursor_name)
     752           0 :         return;
     753             : 
     754             :     /* Find the declared node by declared name */
     755           8 :     p = ecpg_find_declared_statement(declared_name);
     756           8 :     if (p)
     757             :     {
     758           8 :         if (p->cursor_name)
     759           0 :             ecpg_free(p->cursor_name);
     760           8 :         p->cursor_name = ecpg_strdup(cursor_name, lineno);
     761             :     }
     762             : }
     763             : 
     764             : /*
     765             :  * Find and return the connection name referred by the declared name
     766             :  * Input: declared_name -- the name declared in the DECLARE STATEMENT
     767             :  * Return: Found -- The connection name
     768             :  *         Not found -- NULL
     769             :  */
     770             : char *
     771        7240 : ecpg_get_con_name_by_declared_name(const char *declared_name)
     772             : {
     773             :     struct declared_statement *p;
     774             : 
     775        7240 :     p = ecpg_find_declared_statement(declared_name);
     776        7240 :     if (p)
     777          32 :         return p->connection_name;
     778             : 
     779        7208 :     return NULL;
     780             : }
     781             : 
     782             : /*
     783             :  * Find the connection name by referring the declared statements
     784             :  * cursors by using the provided cursor name
     785             :  * Input: cursor_name -- the cursor name
     786             :  * Return: Found -- The connection name
     787             :  *         Not found -- NULL
     788             :  */
     789             : const char *
     790         684 : ecpg_get_con_name_by_cursor_name(const char *cursor_name)
     791             : {
     792             :     struct declared_statement *p;
     793             : 
     794         684 :     if (cursor_name == NULL)
     795           0 :         return NULL;
     796             : 
     797         684 :     p = g_declared_list;
     798        1400 :     while (p)
     799             :     {
     800             :         /* Search the cursor name in the declared list */
     801          64 :         if (p->cursor_name && (strcmp(p->cursor_name, cursor_name) == 0))
     802          32 :             return p->connection_name;
     803             : 
     804          32 :         p = p->next;
     805             :     }
     806             : 
     807         652 :     return NULL;
     808             : }
     809             : 
     810             : /*
     811             :  * Release the declare node from the g_declared_list which refers the connection_name
     812             :  * Input: connection_name -- connection name
     813             :  */
     814             : void
     815         492 : ecpg_release_declared_statement(const char *connection_name)
     816             : {
     817         492 :     struct declared_statement *cur = NULL;
     818         492 :     struct declared_statement *prev = NULL;
     819             : 
     820         492 :     if (connection_name == NULL)
     821           0 :         return;
     822             : 
     823         492 :     cur = g_declared_list;
     824        1000 :     while (cur)
     825             :     {
     826          16 :         if (strcmp(cur->connection_name, connection_name) == 0)
     827             :         {
     828             :             /* If find then release the declared node from the list */
     829           8 :             if (prev)
     830           0 :                 prev->next = cur->next;
     831             :             else
     832           8 :                 g_declared_list = cur->next;
     833             : 
     834           8 :             ecpg_log("ecpg_release_declared_statement: declared name %s is released\n", cur->name);
     835             : 
     836           8 :             ecpg_free(cur->name);
     837           8 :             ecpg_free(cur->connection_name);
     838           8 :             ecpg_free(cur->cursor_name);
     839           8 :             ecpg_free(cur);
     840             : 
     841             :             /*
     842             :              * One connection can be used by multiple declared name, so no
     843             :              * break here
     844             :              */
     845             :         }
     846             :         else
     847           8 :             prev = cur;
     848             : 
     849          16 :         if (prev)
     850           8 :             cur = prev->next;
     851             :         else
     852           8 :             cur = g_declared_list;
     853             :     }
     854             : }

Generated by: LCOV version 1.13