示例#1
0
文件: registry.c 项目: centip3de/GSoC
/**
 * @param [in] reg        the registry to delete the metadata from
 * @param [in] key        the metadata key to delete
 * @param [out] errPtr    on error, a description of the error that occurred
 * @return                true if success; false if failure
 */
int reg_del_metadata(reg_registry* reg, const char* key, reg_error* errPtr) {
    int result = 1;
    sqlite3_stmt* stmt = NULL;
    char* query = "DELETE FROM registry.metadata WHERE key=?";
    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
            && (sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC) == SQLITE_OK)) {
        int r;
        do {
            r = sqlite3_step(stmt);
            switch (r) {
                case SQLITE_DONE:
                    if (sqlite3_changes(reg->db) == 0) {
                        reg_throw(errPtr, REG_INVALID, "no such metadata key");
                        result = 0;
                    } else {
                        sqlite3_reset(stmt);
                    }
                    break;
                case SQLITE_BUSY:
                    break;
                default:
                    reg_sqlite_error(reg->db, errPtr, query);
                    result = 0;
                    break;
            }
        } while (r == SQLITE_BUSY);
    } else {
        reg_sqlite_error(reg->db, errPtr, query);
        result = 0;
    }
    if (stmt) {
        sqlite3_finalize(stmt);
    }
    return result;
}
示例#2
0
文件: registry.c 项目: centip3de/GSoC
/**
 * Detaches a registry database from the registry object. This does some cleanup
 * for an attached registry, then detaches it. Allocated `reg_entry` objects are
 * deleted here.
 *
 * @param [in] reg     registry to detach from
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int reg_detach(reg_registry* reg, reg_error* errPtr) {
    sqlite3_stmt* stmt = NULL;
    int result = 0;
    char* query = "DETACH DATABASE registry";
    if (!(reg->status & reg_attached)) {
        reg_throw(errPtr,REG_MISUSE,"no database is attached to this registry");
        return 0;
    }
    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
        int r;
        reg_entry* entry;
        Tcl_HashEntry* curr;
        Tcl_HashSearch search;
        /* XXX: Busy waiting, consider using sqlite3_busy_handler/timeout */
        do {
            sqlite3_step(stmt);
            r = sqlite3_reset(stmt);
            switch (r) {
                case SQLITE_OK:
                    for (curr = Tcl_FirstHashEntry(&reg->open_entries, &search);
                            curr != NULL; curr = Tcl_NextHashEntry(&search)) {
                        entry = Tcl_GetHashValue(curr);
                        if (entry->proc) {
                            free(entry->proc);
                        }
                        free(entry);
                    }
                    Tcl_DeleteHashTable(&reg->open_entries);
                    for (curr = Tcl_FirstHashEntry(&reg->open_files, &search);
                            curr != NULL; curr = Tcl_NextHashEntry(&search)) {
                        reg_file* file = Tcl_GetHashValue(curr);

                        free(file->proc);
                        free(file->key.path);
                        free(file);
                    }
                    Tcl_DeleteHashTable(&reg->open_files);
                    reg->status &= ~reg_attached;
                    result = 1;
                    break;
                case SQLITE_BUSY:
                    break;
                default:
                    reg_sqlite_error(reg->db, errPtr, query);
                    break;
            }
        } while (r == SQLITE_BUSY);
    } else {
        reg_sqlite_error(reg->db, errPtr, query);
    }
    if (stmt) {
        sqlite3_finalize(stmt);
    }
    return result;
}
示例#3
0
/**
 * Convenience method for returning all objects of a given type from the
 * registry.
 *
 * @param [in] reg       registry to select objects from
 * @param [in] query     the select query to execute
 * @param [in] query_len length of the query (or -1 for automatic)
 * @param [out] objects  the objects selected
 * @param [in] fn        a function to convert sqlite3_stmts to the desired type
 * @param [inout] data   data passed along to the cast function
 * @param [in] del       a function to delete the desired type of object
 * @param [out] errPtr   on error, a description of the error that occurred
 * @return               the number of objects if success; negative if failure
 */
int reg_all_objects(reg_registry* reg, char* query, int query_len,
        void*** objects, cast_function* fn, void* castcalldata,
        free_function* del, reg_error* errPtr) {
    void** results = malloc(10*sizeof(void*));
    int result_count = 0;
    int result_space = 10;
    sqlite3_stmt* stmt = NULL;
    if (!results || !fn) {
        return -1;
    }
    if (sqlite3_prepare_v2(reg->db, query, query_len, &stmt, NULL) == SQLITE_OK) {
        int r;
        void* row;
        do {
            r = sqlite3_step(stmt);
            switch (r) {
                case SQLITE_ROW:
                    if (fn(reg, &row, stmt, castcalldata, errPtr)) {
                        if (!reg_listcat(&results, &result_count, &result_space, row)) {
                            r = SQLITE_ERROR;
                        }
                    } else {
                        r = SQLITE_ERROR;
                    }
                    break;
                case SQLITE_DONE:
                    break;
                case SQLITE_BUSY:
                    continue;
                default:
                    reg_sqlite_error(reg->db, errPtr, query);
                    break;
            }
        } while (r == SQLITE_ROW || r == SQLITE_BUSY);
        sqlite3_finalize(stmt);
        if (r == SQLITE_DONE) {
            *objects = results;
            return result_count;
        } else if (del) {
            int i;
            for (i=0; i<result_count; i++) {
                del(NULL, results[i]);
            }
        }
    } else {
        if (stmt) {
            sqlite3_finalize(stmt);
        }
        reg_sqlite_error(reg->db, errPtr, query);
    }
    free(results);
    return -1;
}
示例#4
0
文件: registry.c 项目: centip3de/GSoC
/**
 * Creates a new registry object. To start using a registry, one must first be
 * attached with `reg_attach`.
 *
 * @param [out] regPtr address of the allocated registry
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int reg_open(reg_registry** regPtr, reg_error* errPtr) {
    reg_registry* reg = malloc(sizeof(reg_registry));
    if (!reg) {
        return 0;
    }
    if (sqlite3_open(NULL, &reg->db) == SQLITE_OK) {
        /* Enable extended result codes, requires SQLite >= 3.3.8
         * Check added for compatibility with Tiger. */
#if SQLITE_VERSION_NUMBER >= 3003008
        sqlite3_extended_result_codes(reg->db, 1);
#endif

        sqlite3_busy_timeout(reg->db, 25);

        if (init_db(reg->db, errPtr)) {
            reg->status = reg_none;
            *regPtr = reg;
            return 1;
        }
    } else {
        reg_sqlite_error(reg->db, errPtr, NULL);
    }
    sqlite3_close(reg->db);
    free(reg);
    return 0;
}
示例#5
0
/**
 * Executes a null-terminated list of queries. Pass it a list of queries, it'll
 * execute them. This is mainly intended for initialization, when you have a
 * number of standard queries to execute.
 *
 * @param [in] db      database to execute queries on
 * @param [in] queries NULL-terminated list of queries
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int do_queries(sqlite3* db, char** queries, reg_error* errPtr) {
    char** query;
    sqlite3_stmt* stmt = NULL;
    int r = SQLITE_OK;

    for (query = queries; *query != NULL; query++) {
        if ((r = sqlite3_prepare_v2(db, *query, -1, &stmt, NULL)) != SQLITE_OK) {
            sqlite3_finalize(stmt);
            break;
        }

        do {
            r = sqlite3_step(stmt);
        } while (r == SQLITE_BUSY);

        sqlite3_finalize(stmt);

        /* Either execution succeeded and r == SQLITE_DONE | SQLITE_ROW, or there was an error */
        if (r != SQLITE_DONE && r != SQLITE_ROW) {
            /* stop executing statements in case of errors */
            break;
        }
    }

    switch (r) {
        case SQLITE_OK:
        case SQLITE_DONE:
        case SQLITE_ROW:
            return 1;
        default:
            /* handle errors */
            reg_sqlite_error(db, errPtr, *query);
            return 0;
    }
}
示例#6
0
文件: registry.c 项目: centip3de/GSoC
/**
 * @param [in] reg     registry to set value in
 * @param [in] key     metadata key to set
 * @param [in] value   the desired value for the key
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int reg_set_metadata(reg_registry* reg, const char* key, const char* value,
        reg_error* errPtr) {
    int result = 0;
    sqlite3_stmt* stmt = NULL;
    char* query;
    char *test_value;
    int get_returnval = reg_get_metadata(reg, key, &test_value, errPtr);
    if (get_returnval) {
        free(test_value);
        query = sqlite3_mprintf("UPDATE registry.metadata SET value = '%q' WHERE key='%q'",
            value, key);
    } else if (errPtr->code == REG_NOT_FOUND) {
        query = sqlite3_mprintf("INSERT INTO registry.metadata (key, value) VALUES ('%q', '%q')",
            key, value);
    } else {
        return get_returnval;
    }
    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
        int r;
        do {
            r = sqlite3_step(stmt);
            switch (r) {
                case SQLITE_DONE:
                    result = 1;
                    break;
                case SQLITE_BUSY:
                    break;
                default:
                    reg_sqlite_error(reg->db, errPtr, query);
                    break;
            }
        } while (r == SQLITE_BUSY);
    } else {
        reg_sqlite_error(reg->db, errPtr, query);
    }
    if (stmt) {
        sqlite3_finalize(stmt);
    }
    sqlite3_free(query);
    return result;
}
示例#7
0
文件: registry.c 项目: centip3de/GSoC
/**
 * @param [in] reg     registry to get value from
 * @param [in] key     metadata key to get
 * @param [out] value  the value of the metadata
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int reg_get_metadata(reg_registry* reg, const char* key, char** value,
        reg_error* errPtr) {
    int result = 0;
    sqlite3_stmt* stmt = NULL;
    char* query = "SELECT value FROM registry.metadata WHERE key=?";
    const char *text;
    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK
            && (sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC) == SQLITE_OK)) {
        int r;
        do {
            r = sqlite3_step(stmt);
            switch (r) {
                case SQLITE_ROW:
                    text = (const char*)sqlite3_column_text(stmt, 0);
                    if (text) {
                        *value = strdup(text);
                        result = 1;
                    } else {
                        reg_sqlite_error(reg->db, errPtr, query);
                    }
                    break;
                case SQLITE_DONE:
                    errPtr->code = REG_NOT_FOUND;
                    errPtr->description = "no such key in metadata";
                    errPtr->free = NULL;
                    break;
                case SQLITE_BUSY:
                    continue;
                default:
                    reg_sqlite_error(reg->db, errPtr, query);
                    break;
            }
        } while (r == SQLITE_BUSY);
    } else {
        reg_sqlite_error(reg->db, errPtr, query);
    }
    if (stmt) {
        sqlite3_finalize(stmt);
    }
    return result;
}
示例#8
0
文件: sql.c 项目: nghiwill/macports
/**
 * Executes a null-terminated list of queries. Pass it a list of queries, it'll
 * execute them. This is mainly intended for initialization, when you have a
 * number of standard queries to execute.
 *
 * @param [in] db      database to execute queries on
 * @param [in] queries NULL-terminated list of queries
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int do_queries(sqlite3* db, char** queries, reg_error* errPtr) {
    char** query;
    for (query = queries; *query != NULL; query++) {
        sqlite3_stmt* stmt = NULL;
        if ((sqlite3_prepare(db, *query, -1, &stmt, NULL) != SQLITE_OK)
                || (sqlite3_step(stmt) != SQLITE_DONE)) {
            reg_sqlite_error(db, errPtr, *query);
            if (stmt) {
                sqlite3_finalize(stmt);
            }
            return 0;
        }
        sqlite3_finalize(stmt);
    }
    return 1;
}
示例#9
0
文件: registry.c 项目: centip3de/GSoC
/**
 * Helper function for `reg_commit` and `reg_rollback`.
 */
static int reg_end(reg_registry* reg, const char* query, reg_error* errPtr, int is_rollback) {
    if (!(reg->status & reg_transacting)) {
        reg_throw(errPtr, REG_MISUSE, "couldn't end transaction because no "
                "transaction is open");
        return 0;
    } else {
        int r;
        do {
            r = sqlite3_exec(reg->db, query, NULL, NULL, NULL);
            if (r == SQLITE_OK) {
                return 1;
            }
        } while (r == SQLITE_BUSY && !is_rollback);
        reg_sqlite_error(reg->db, errPtr, NULL);
        return 0;
    }
}
示例#10
0
文件: registry.c 项目: centip3de/GSoC
/**
 * Helper function for `reg_start_read` and `reg_start_write`.
 */
static int reg_start(reg_registry* reg, const char* query, reg_error* errPtr) {
    if (reg->status & reg_transacting) {
        reg_throw(errPtr, REG_MISUSE, "couldn't start transaction because a "
                "transaction is already open");
        errPtr->free = NULL;
        return 0;
    } else {
        int r;
        do {
            r = sqlite3_exec(reg->db, query, NULL, NULL, NULL);
            if (r == SQLITE_OK) {
                return 1;
            }
        } while (r == SQLITE_BUSY);
        reg_sqlite_error(reg->db, errPtr, NULL);
        return 0;
    }
}
示例#11
0
/**
 * Creates a new registry object. To start using a registry, one must first be
 * attached with `reg_attach`.
 *
 * @param [out] regPtr address of the allocated registry
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int reg_open(reg_registry** regPtr, reg_error* errPtr) {
    reg_registry* reg = malloc(sizeof(reg_registry));
    if (!reg) {
        return 0;
    }
    if (sqlite3_open(NULL, &reg->db) == SQLITE_OK) {
        if (init_db(reg->db, errPtr)) {
            reg->status = reg_none;
            *regPtr = reg;
            return 1;
        }
    } else {
        reg_sqlite_error(reg->db, errPtr, NULL);
    }
    sqlite3_close(reg->db);
    free(reg);
    return 0;
}
示例#12
0
文件: registry.c 项目: centip3de/GSoC
/**
 * Attaches a registry database to the registry object. Prior to calling this,
 * the registry object is not actually connected to the registry. This function
 * attaches it so it can be queried and manipulated.
 *
 * @param [in] reg     the registry to attach to
 * @param [in] path    path to the registry db on disk
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int reg_attach(reg_registry* reg, const char* path, reg_error* errPtr) {
    struct stat sb;
    int initialized = 1; /* registry already exists */
    int can_write = 1; /* can write to this location */
    int result = 0;
    if (reg->status & reg_attached) {
        reg_throw(errPtr, REG_MISUSE, "a database is already attached to this "
                "registry");
        return 0;
    }
    if (stat(path, &sb) != 0) {
        initialized = 0;
        if (errno == ENOENT) {
            char *dirc, *dname;
            dirc = strdup(path);
            dname = dirname(dirc);
            if (stat(dname, &sb) != 0) {
                can_write = 0;
            }
            free(dirc);
        } else {
            can_write = 0;
        }
    }
    /* can_write is still true if one of the stat calls succeeded */
    if (initialized || can_write) {
        sqlite3_stmt* stmt = NULL;
        char* query = sqlite3_mprintf("ATTACH DATABASE '%q' AS registry", path);
        int r;
        do {
            r = sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL);
        } while (r == SQLITE_BUSY);
        if (r == SQLITE_OK) {
            /* XXX: Busy waiting, consider using sqlite3_busy_handler/timeout */
            do {
                sqlite3_step(stmt);
                r = sqlite3_reset(stmt);
                switch (r) {
                    case SQLITE_OK:
                        if (initialized || (create_tables(reg->db, errPtr))) {
                            Tcl_InitHashTable(&reg->open_entries,
                                    sizeof(sqlite_int64)/sizeof(int));
                            Tcl_InitHashTable(&reg->open_files,
                                    TCL_STRING_KEYS);
                            Tcl_InitHashTable(&reg->open_portgroups,
                                    sizeof(sqlite_int64)/sizeof(int));
                            reg->status |= reg_attached;
                            result = 1;
                        }
                        break;
                    case SQLITE_BUSY:
                        break;
                    default:
                        reg_sqlite_error(reg->db, errPtr, query);
                }
            } while (r == SQLITE_BUSY);

            sqlite3_finalize(stmt);
            stmt = NULL;

            if (result) {
                result &= update_db(reg->db, errPtr);
            }
        } else {
            reg_sqlite_error(reg->db, errPtr, query);
        }
        if (stmt) {
            sqlite3_finalize(stmt);
        }
        sqlite3_free(query);
    } else {
        reg_throw(errPtr, REG_CANNOT_INIT, "port registry doesn't exist at "
                "\"%q\" and couldn't write to this location", path);
    }
    return result;
}
示例#13
0
/**
 * Updates the database if necessary. This function queries the current database version
 * from the metadata table and executes SQL to update the schema to newer versions if needed.
 * After that, this function updates the database version number
 *
 * @param [in] db      database to update
 * @param [out] errPtr on error, a description of the error that occurred
 * @return             true if success; false if failure
 */
int update_db(sqlite3* db, reg_error* errPtr) {
    const char* version;
    int r;
    int did_update = 0; /* true, if an update was done and the loop should be run again */
    char* q_begin = "BEGIN";
    char* q_version = "SELECT value FROM registry.metadata WHERE key = 'version'";
    char* query = q_begin;
    sqlite3_stmt* stmt = NULL;

    do {
        did_update = 0;

        /* open a transaction to prevent a check-and-change race condition between
         * multiple port(1) instances */
        if ((r = sqlite3_prepare_v2(db, query, -1, &stmt, NULL)) != SQLITE_OK) {
            break;
        }

        if ((r = sqlite3_step(stmt)) != SQLITE_DONE) {
            break;
        }

        sqlite3_finalize(stmt);
        stmt = NULL;

        /* query current version number */
        query = q_version;
        if ((r = sqlite3_prepare_v2(db, query, -1, &stmt, NULL)) != SQLITE_OK) {
            break;
        }

        r = sqlite3_step(stmt);
        if (r == SQLITE_DONE) {
            /* the version number was not found */
            reg_throw(errPtr, REG_INVALID, "Version number in metadata table not found.");
            sqlite3_finalize(stmt);
            rollback_db(db);
            return 0;
        }
        if (r != SQLITE_ROW) {
            /* an error occured querying */
            break;
        }
        if (NULL == (version = (const char *)sqlite3_column_text(stmt, 0))) {
            reg_throw(errPtr, REG_INVALID, "Version number in metadata table is NULL.");
            sqlite3_finalize(stmt);
            rollback_db(db);
            return 0;
        }

        /* we can't call vercmp directly because it's static, but we have
         * sql_version, which is basically an alias */
        if (sql_version(NULL, -1, version, -1, "1.1") < 0) {
            /* we need to update to 1.1, add binary field and index to files
             * table */
            static char* version_1_1_queries[] = {
#if SQLITE_VERSION_NUMBER >= 3002000
                "ALTER TABLE registry.files ADD COLUMN binary BOOL",
#else
                /*
                 * SQLite < 3.2.0 doesn't support ALTER TABLE ADD COLUMN
                 * Unfortunately, Tiger ships with SQLite < 3.2.0 (#34463)
                 * This is taken from http://www.sqlite.org/faq.html#q11
                 */

                /* Create a temporary table */
                "CREATE TEMPORARY TABLE mp_files_backup (id INTEGER, path TEXT, "
                    "actual_path TEXT, active INT, mtime DATETIME, md5sum TEXT, editable INT, "
                    "FOREIGN KEY(id) REFERENCES ports(id))",

                /* Copy all data into the temporary table */
                "INSERT INTO mp_files_backup SELECT id, path, actual_path, active, mtime, "
                    "md5sum, editable FROM registry.files",

                /* Drop the original table and re-create it with the new structure */
                "DROP TABLE registry.files",
                "CREATE TABLE registry.files (id INTEGER, path TEXT, actual_path TEXT, "
                    "active INT, mtime DATETIME, md5sum TEXT, editable INT, binary BOOL, "
                    "FOREIGN KEY(id) REFERENCES ports(id))",
                "CREATE INDEX registry.file_port ON files(id)",
                "CREATE INDEX registry.file_path ON files(path)",
                "CREATE INDEX registry.file_actual ON files(actual_path)",

                /* Copy all data back from temporary table */
                "INSERT INTO registry.files (id, path, actual_path, active, mtime, md5sum, "
                    "editable) SELECT id, path, actual_path, active, mtime, md5sum, "
                    "editable FROM mp_files_backup",

                /* Remove temporary table */
                "DROP TABLE mp_files_backup",
#endif
                "CREATE INDEX registry.file_binary ON files(binary)",

                "UPDATE registry.metadata SET value = '1.100' WHERE key = 'version'",

                "COMMIT",
                NULL
            };

            /* don't forget to finalize the version query here, or it might
             * cause "cannot commit transaction - SQL statements in progress",
             * see #32686 */
            sqlite3_finalize(stmt);
            stmt = NULL;

            if (!do_queries(db, version_1_1_queries, errPtr)) {
                rollback_db(db);
                return 0;
            }

            did_update = 1;
            continue;
        }

        /* add new versions here, but remember to:
         *  - finalize the version query statement and set stmt to NULL
         *  - do _not_ use "BEGIN" in your query list, since a transaction has
         *    already been started for you
         *  - end your query list with "COMMIT", NULL
         *  - set did_update = 1 and continue;
         */

        /* if we arrive here, no update was done and we should end the
         * transaction. Using ROLLBACK here causes problems when rolling back
         * other transactions later in the program. */
        sqlite3_finalize(stmt);
        stmt = NULL;
        r = sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
    } while (did_update);

    sqlite3_finalize(stmt);
    switch (r) {
        case SQLITE_OK:
        case SQLITE_DONE:
        case SQLITE_ROW:
            return 1;
        default:
            reg_sqlite_error(db, errPtr, query);
            return 0;
    }
}