/*
** This is the top-level implementation of sqlite4_step().  Call
** sqlite4Step() to do most of the work.  If a schema error occurs,
** call sqlite4Reprepare() and try again.
*/
int sqlite4_step(sqlite4_stmt *pStmt) {
    int rc = SQLITE4_OK;      /* Result from sqlite4Step() */
    int rc2 = SQLITE4_OK;     /* Result from sqlite4Reprepare() */
    Vdbe *v = (Vdbe*)pStmt;  /* the prepared statement */
    int cnt = 0;             /* Counter to prevent infinite loop of reprepares */
    sqlite4 *db;             /* The database connection */

    if( vdbeSafetyNotNull(v) ) {
        return SQLITE4_MISUSE_BKPT;
    }
    db = v->db;
    sqlite4_mutex_enter(db->mutex);
    while( (rc = sqlite4Step(v))==SQLITE4_SCHEMA
            && cnt++ < SQLITE4_MAX_SCHEMA_RETRY
            && (rc2 = rc = sqlite4Reprepare(v))==SQLITE4_OK ) {
        sqlite4_reset(pStmt);
        assert( v->expired==0 );
    }
    if( rc2!=SQLITE4_OK && ALWAYS(db->pErr) ) {
        /* This case occurs after failing to recompile an sql statement.
        ** The error message from the SQL compiler has already been loaded
        ** into the database handle. This block copies the error message
        ** from the database handle into the statement and sets the statement
        ** program counter to 0 to ensure that when the statement is
        ** finalized or reset the parser error message is available via
        ** sqlite4_errmsg() and sqlite4_errcode().
        */
        const char *zErr = sqlite4_value_text(db->pErr, 0);
        sqlite4DbFree(db, v->zErrMsg);
        if( !db->mallocFailed ) {
            v->zErrMsg = sqlite4DbStrDup(db, zErr);
            v->rc = rc2;
        } else {
            v->zErrMsg = 0;
            v->rc = rc = SQLITE4_NOMEM;
        }
    }
    rc = sqlite4ApiExit(db, rc);
    sqlite4_mutex_leave(db->mutex);
    return rc;
}
/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
**
** This routine implements the bulk of the logic behind the sqlite_step()
** API.  The only thing omitted is the automatic recompile if a
** schema change has occurred.  That detail is handled by the
** outer sqlite4_step() wrapper procedure.
*/
static int sqlite4Step(Vdbe *p) {
    sqlite4 *db;
    int rc;

    assert(p);
    if( p->magic!=VDBE_MAGIC_RUN ) {
        /* We used to require that sqlite4_reset() be called before retrying
        ** sqlite4_step() after any error or after SQLITE4_DONE.  But beginning
        ** with version 3.7.0, we changed this so that sqlite4_reset() would
        ** be called automatically instead of throwing the SQLITE4_MISUSE error.
        ** This "automatic-reset" change is not technically an incompatibility,
        ** since any application that receives an SQLITE4_MISUSE is broken by
        ** definition.
        **
        ** Nevertheless, some published applications that were originally written
        ** for version 3.6.23 or earlier do in fact depend on SQLITE4_MISUSE
        ** returns, and those were broken by the automatic-reset change.  As a
        ** a work-around, the SQLITE4_OMIT_AUTORESET compile-time restores the
        ** legacy behavior of returning SQLITE4_MISUSE for cases where the
        ** previous sqlite4_step() returned something other than a SQLITE4_LOCKED
        ** or SQLITE4_BUSY error.
        */
#ifdef SQLITE4_OMIT_AUTORESET
        if( p->rc==SQLITE4_BUSY || p->rc==SQLITE4_LOCKED ) {
            sqlite4_reset((sqlite4_stmt*)p);
        } else {
            return SQLITE4_MISUSE_BKPT;
        }
#else
        sqlite4_reset((sqlite4_stmt*)p);
#endif
    }

    /* Check that malloc() has not failed. If it has, return early. */
    db = p->db;
    if( db->mallocFailed ) {
        p->rc = SQLITE4_NOMEM;
        return SQLITE4_NOMEM;
    }

    if( p->pc<=0 && p->expired ) {
        p->rc = SQLITE4_SCHEMA;
        rc = SQLITE4_ERROR;
        goto end_of_step;
    }
    if( p->pc<0 ) {
        /* If there are no other statements currently running, then
        ** reset the interrupt flag.  This prevents a call to sqlite4_interrupt
        ** from interrupting a statement that has not yet started.
        */
        if( db->activeVdbeCnt==0 ) {
            db->u1.isInterrupted = 0;
        }

        assert( db->writeVdbeCnt>0 || db->pSavepoint || db->nDeferredCons==0 );

#ifndef SQLITE4_OMIT_TRACE
        if( db->xProfile && !db->init.busy ) {
            sqlite4OsCurrentTime(0, &p->startTime);
        }
#endif

        db->activeVdbeCnt++;
        if( p->readOnly==0 ) db->writeVdbeCnt++;
        p->pc = 0;
    }
#ifndef SQLITE4_OMIT_EXPLAIN
    if( p->explain ) {
        rc = sqlite4VdbeList(p);
    } else
#endif /* SQLITE4_OMIT_EXPLAIN */
    {
        db->vdbeExecCnt++;
        rc = sqlite4VdbeExec(p);
        db->vdbeExecCnt--;
    }

#ifndef SQLITE4_OMIT_TRACE
    /* Invoke the profile callback if there is one
    */
    if( rc!=SQLITE4_ROW && db->xProfile && !db->init.busy && p->zSql ) {
        sqlite4_uint64 iNow = 0;
        sqlite4OsCurrentTime(0, &iNow);
        db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
    }
#endif

    db->errCode = rc;
    if( SQLITE4_NOMEM==sqlite4ApiExit(p->db, p->rc) ) {
        p->rc = SQLITE4_NOMEM;
    }
end_of_step:
    /* At this point local variable rc holds the value that should be
    ** returned if this statement was compiled using the legacy
    ** sqlite4_prepare() interface. According to the docs, this can only
    ** be one of the values in the first assert() below. Variable p->rc
    ** contains the value that would be returned if sqlite4_finalize()
    ** were called on statement p.
    */
    assert( rc==SQLITE4_ROW  || rc==SQLITE4_DONE   || rc==SQLITE4_ERROR
            || rc==SQLITE4_BUSY || rc==SQLITE4_MISUSE
          );
    assert( p->rc!=SQLITE4_ROW && p->rc!=SQLITE4_DONE );
    if( rc!=SQLITE4_ROW && rc!=SQLITE4_DONE ) {
        rc = sqlite4VdbeTransferError(p);
    }
    return rc;
}
예제 #3
0
파일: freeqd.c 프로젝트: GooseYArd/freeq
int tbl_to_db(struct freeq_ctx *ctx, struct freeq_table *tbl, sqlite4 *mDb)
{
        sqlite4_stmt *stmt;
        GString *sql = g_string_sized_new(255);
        GSList *colp[tbl->numcols];

        memset(colp, 0, tbl->numcols * sizeof(GSList *) );
        for (int j = 0; j < tbl->numcols; j++)
                colp[j] = tbl->columns[j].data;

        int res;

        freeq_table_print(ctx, tbl, stdout);

        if (sqlite4_exec(mDb, "BEGIN TRANSACTION;", NULL, NULL) != SQLITE4_OK)
        {
                dbg(ctx, "unable to start transaction: %s\n", sqlite4_errmsg(mDb));
                return 1;
        }

        g_string_printf(sql, "DROP TABLE %s;", tbl->name);
        if (sqlite4_exec(mDb, sql->str, NULL, NULL) != SQLITE4_OK)
                dbg(ctx, "failed to drop table, ignoring\n");

        table_ddl(ctx, tbl, sql);
        if (sqlite4_exec(mDb, sql->str, NULL, NULL) != SQLITE4_OK)
        {
                dbg(ctx, "failed to create table, rolling back\n");
                sqlite4_exec(mDb, "ROLLBACK;", NULL, NULL);
                g_string_free(sql, 1);
                return 1;
        }

        ddl_insert(ctx, tbl, sql);
        if ((res = sqlite4_prepare(mDb, sql->str, sql->len, &stmt, NULL)) != SQLITE4_OK)
        {
                dbg(ctx, "failed to create statement (%d), rolling back\n", res);
                sqlite4_exec(mDb, "ROLLBACK;", NULL, NULL);
                g_string_free(sql,1);
                return 1;
        }

        g_string_free(sql,1);
        for (uint32_t i = 0; i < tbl->numrows; i++)
        {
                for (uint32_t j = 0; j < tbl->numcols; j++)
                {
                        switch (tbl->columns[j].coltype)
                        {
                        case FREEQ_COL_STRING:
                                res = sqlite4_bind_text(stmt,
                                                        j+1,
                                                        colp[j]->data == NULL ? "" : colp[j]->data,
                                                        colp[j]->data == NULL ? 0 : strlen(colp[j]->data),
                                                        SQLITE4_TRANSIENT, NULL);
                                if (res != SQLITE4_OK)
                                {
                                        dbg(ctx, "stmt: %s\n", (char *)stmt);
                                        dbg(ctx, "row %d failed binding string column %d %s: %s (%d)\n", i, j, (char *)colp[j]->data, sqlite4_errmsg(mDb), res);
                                }
                                break;
                        case FREEQ_COL_NUMBER:
                                res = sqlite4_bind_int(stmt, j, GPOINTER_TO_INT(colp[j]->data));
                                if (res != SQLITE4_OK)
                                {
                                        dbg(ctx, "row %d failed bind: %s\n", i, sqlite4_errmsg(mDb));
                                }
                                break;
                        default:
                                break;
                        }
                        colp[j] = g_slist_next(colp[j]);
                }
                if (sqlite4_step(stmt) != SQLITE4_DONE)
                {
                        dbg(ctx, "execute failed: %s\n", sqlite4_errmsg(mDb));
                        sqlite4_exec(mDb, "ROLLBACK;", NULL, NULL);
                        sqlite4_finalize(stmt);
                        return -1;
                } else {
                        sqlite4_reset(stmt);
                }
        }

        dbg(ctx, "committing transaction\n");
        res = sqlite4_exec(mDb, "COMMIT TRANSACTION;", NULL, NULL);
        dbg(ctx, "result of commit was %d\n", res);
        sqlite4_finalize(stmt);
        return 0;
}