Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add internal structure #296

Merged
merged 7 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ ${CLI} ${STANDALONE_PFX}2json${EXE}: MORE_OBJECTS+= ${BUILD_DIR}/objs/utils/db.o
# pretty uses termcap
${CLI} ${STANDALONE_PFX}pretty${EXE}: MORE_LIBS+=${LDFLAGS_TERMCAP}


${STANDALONE_PFX}%${EXE}: %.c ${OBJECTS} ${MORE_OBJECTS} ${LIBZSV_INSTALL} ${UTF8PROC_OBJECT}
@mkdir -p `dirname "$@"`
${CC} ${CFLAGS} -I${INCLUDE_DIR} -o $@ $< ${OBJECTS} ${MORE_OBJECTS} ${MORE_SOURCE} -L${LIBDIR} ${LIBZSV_L} ${UTF8PROC_OBJECT} ${LDFLAGS} ${LDFLAGS_OPT} ${MORE_LIBS} ${STATIC_LIB_FLAGS}
Expand Down
8 changes: 5 additions & 3 deletions app/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,11 @@ static struct builtin_cmd *find_builtin(const char *cmd_name) {
#include "builtin/version.c"
#include "builtin/register.c"

#define ZSV_EXTENSION_ID_MAX_LEN 8
static const char *extension_cmd_from_arg(const char *arg) {
if (strlen(arg) > 3 && arg[2] == '-')
return arg + 3;
const char *dash = strchr(arg, '-');
if (dash && dash < arg + ZSV_EXTENSION_ID_MAX_LEN && dash[1] != '\0')
return dash + 1;
return NULL;
}

Expand Down Expand Up @@ -567,7 +569,7 @@ int ZSV_CLI_MAIN(int argc, const char *argv[]) {
}

int err = 1;
if (strlen(argv[1]) > 3 && argv[1][2] == '-') { // this is an extension command
if (extension_cmd_from_arg(argv[1]) != NULL) { // this is an extension command
struct cli_config config;
memset(&config, 0, sizeof(config));
if (!(err = add_extension(argv[1], &config.extensions, 0, 0)))
Expand Down
14 changes: 7 additions & 7 deletions app/ext_example/my_extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/**
* `zsv` can easily be extended by simply creating a shared library
* that implements the interface specified in zsv/ext/implementation.h
* for any two-character extension id
* for any extension id of up to 8 bytes
*
* Once the library file is created, you can run any commands it implements
* by naming the library file zsvext<id>, placing it in any folder that is
Expand All @@ -30,21 +30,21 @@
*
* We will name our extension "my", so our shared library will be named
* zsvextmy.so (non-win) or zsvextmy.dll (win). After the shared lib is built,
* a user can place it anywhere in their path or in the same folder as the zsv
* binary, and invoke our operations as follows:
* place it anywhere in the PATH or in the same folder as the zsv binary.
* Our extension commands can then be invoked by running:
* `zsv my-count`
* `zsv my-echo`
*
* in addition, users will see a brief description of our module if they execute:
* in addition, a description of our extension is available via:
* `zsv help`
*
* or
* and command-specific help displayed via:
* `zsv help my-<command>`
*
*/

/**
* *Required*: define our extension id, which must be two characters in length
* *Required*: define our extension id, of up to 8 bytes in length
*/
const char *zsv_ext_id(void) {
return "my";
Expand Down Expand Up @@ -114,7 +114,7 @@ zsvsheet_status my_test_command_handler(zsvsheet_proc_context_t ctx) {
* initialization routine uses `ext_add_command` to register our commands and
* `ext_set_help` to set the help text. When we register a command, we provide a
* callback-- in our cases, those will be `count_main()` and `echo_main()`-- for
* zsv to invoke when a user runs our command
* zsv to invoke when our command is run
*
* @param callbacks pointers to zsvlib functions that we must save for later use
* @param ctx context to be passed whenever we execute a zsvlib function from our init
Expand Down
147 changes: 147 additions & 0 deletions app/external/sqlite3/sqlite3_csv_vtab-mem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#ifndef SQLITE3_CSV_VTAB_ZSV_H
#define SQLITE3_CSV_VTAB_ZSV_H

#include <pthread.h>

/**
* see sqlite3_csv_vtab-mem.h for background info
*/
#if defined(_WIN32) || defined(_WIN64)
#include <process.h>
#else
#include <unistd.h>
#endif

struct sqlite3_zsv_data {
struct sqlite3_zsv_data *next;
pid_t pid;
char *filename;
struct zsv_opts opts;
struct zsv_prop_handler custom_prop_handler;
};

pthread_mutex_t sqlite3_zsv_data_mutex;
struct sqlite3_zsv_data *sqlite3_zsv_data_g = NULL;

/**
* Our shared memory structure should be locked for read/write
*/
static int sqlite3_zsv_data_lock(void) {
#ifndef NO_THREADING
pthread_mutex_lock(&sqlite3_zsv_data_mutex);
#endif
return 0;
}

static int sqlite3_zsv_data_unlock(void) {
#ifndef NO_THREADING
pthread_mutex_unlock(&sqlite3_zsv_data_mutex);
#endif
return 0;
}

static void sqlite3_zsv_data_delete(struct sqlite3_zsv_data *e) {
if (e) {
free(e->filename);
}
free(e);
}

void sqlite3_zsv_list_delete(void **list) {
for (struct sqlite3_zsv_data *next, *e = *list; e; e = next) {
next = e->next;
sqlite3_zsv_data_delete(e);
}
#ifndef NO_THREADING
pthread_mutex_destroy(&sqlite3_zsv_data_mutex);
#endif
*list = NULL;
}

static struct sqlite3_zsv_data *sqlite3_zsv_data_new(const char *filename, struct zsv_opts *opts,
struct zsv_prop_handler *custom_prop_handler) {
if (!filename)
return NULL;
struct sqlite3_zsv_data *e = calloc(1, sizeof(*e));
if (e) {
e->pid = getpid();
e->filename = strdup(filename);
if (opts)
e->opts = *opts;
if (custom_prop_handler)
e->custom_prop_handler = *custom_prop_handler;
if (e->filename)
return e;
}
sqlite3_zsv_data_delete(e);
return NULL;
}

int sqlite3_zsv_data_add(const char *filename, struct zsv_opts *opts, struct zsv_prop_handler *custom_prop_handler) {
struct sqlite3_zsv_data **list = &sqlite3_zsv_data_g;
struct sqlite3_zsv_data *e = sqlite3_zsv_data_new(filename, opts, custom_prop_handler);
if (e) {
struct sqlite3_zsv_data **next;
if (sqlite3_zsv_data_lock()) {
sqlite3_zsv_data_delete(e);
return -1;
} else {
for (next = list; *next; next = &(*next)->next)
;
*next = e;
sqlite3_zsv_data_unlock();
return 0;
}
}
return ENOMEM;
}

static int sqlite3_zsv_data_cmp(struct sqlite3_zsv_data *x, const char *filename, pid_t pid) {
return strcmp(x->filename, filename) && x->pid == pid;
}

struct sqlite3_zsv_data *sqlite3_zsv_data_find(const char *filename) {
struct sqlite3_zsv_data *list = sqlite3_zsv_data_g;
struct sqlite3_zsv_data *found = NULL;
pid_t pid = getpid();
if (!sqlite3_zsv_data_lock()) {
for (struct sqlite3_zsv_data *e = list; e && !found; e = e->next) {
if (!sqlite3_zsv_data_cmp(e, filename, pid))
found = e;
}
if (sqlite3_zsv_data_unlock())
fprintf(stderr, "Error unlocking sqlite3-csv-zsv shared mem lock\n");
}
return found;
}

int sqlite3_zsv_list_remove(const char *filename) {
if (!filename)
return 0;
struct sqlite3_zsv_data **list = &sqlite3_zsv_data_g;
struct sqlite3_zsv_data *found = NULL;
pid_t pid = getpid();
if (*list) {
if (!sqlite3_zsv_data_cmp(*list, filename, pid)) {
// found a match at the head of list
found = *list;
*list = found->next;
} else {
// look for a match somewhere after the first element
for (struct sqlite3_zsv_data *prior = *list; prior->next != NULL; prior = prior->next) {
if (!sqlite3_zsv_data_cmp(prior->next, filename, pid)) {
found = prior->next;
prior->next = prior->next->next;
break;
}
}
}
}
if (found) {
sqlite3_zsv_data_delete(found);
return 0;
}
return ENOENT; // not found
}

#endif
49 changes: 49 additions & 0 deletions app/external/sqlite3/sqlite3_csv_vtab-mem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#ifndef SQLITE3_CSV_VTAB_ZSV_H
#define SQLITE3_CSV_VTAB_ZSV_H

#include <zsv.h>
#include <zsv/utils/prop.h>

/**
* when sqlite3 opens a CSV file using ZSV, it needs a way to know
* what options to open with (such as user-specified delimiter, header offset
* or span, etc
*
* In particular, it needs access to:
* - zsv options (struct zsv_opts)
* - custom property handler (struct zsv_prop_handler *)
* - options used (const char *) [but this can be passed via connection string]
*
* Some ways to pass this info are:
* - Embed it in the text of the URI passed to the module's xConnect function.
* This is not practical because we need to pass pointers
* - Use a single global variable that can hold only one set of data at a time.
* This was the old approach, via `zsv_set_default_opts` etc, which has the
* usual drawbacks of using a single global variable structure
* - Use a shared memory structure that can support multiple sets of data
* That is the approach implemented here. Data is identified by the related
* filename and caller pid
*
* sqlite3_create_module_v2 is passed the shared memory root pointer,
* but it's not really needed because there is no way for it to be
* dynamic so it always has to point to the single global location
*
* Prior to calling xConnect, the caller should save data for the related
* file via `sqlite3_zsv_data_add()`; xConnect then does a lookup to
* locate and use the saved data
*/

struct sqlite3_zsv_data;

void sqlite3_zsv_list_delete(struct sqlite3_zsv_data **list);

int sqlite3_zsv_data_add(const char *filename, struct zsv_opts *opts, struct zsv_prop_handler *custom_prop_handler);

struct sqlite3_zsv_data *sqlite3_csv_vtab_zsv_find(const char *filename);

/**
* Remove from list. Return 0 on success, non-zero on error
*/
int sqlite3_zsv_list_remove(const char *filename);

#endif
44 changes: 27 additions & 17 deletions app/external/sqlite3/sqlite3_csv_vtab-zsv.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* clang-format off */
/*
* This file has been modified from its original form, in order to use the ZSV csv parser
* The preamble / disclaimer to the original file is included below
Expand Down Expand Up @@ -51,6 +52,7 @@ SQLITE_EXTENSION_INIT1
#include <zsv/utils/string.h>
#include <zsv/utils/arg.h>
#include <zsv/utils/prop.h>
#include "sqlite3_csv_vtab-mem.c"

#ifndef SQLITE_OMIT_VIRTUALTABLE

Expand Down Expand Up @@ -102,12 +104,15 @@ typedef struct zsvTable {
sqlite_int64 rowCount;
} zsvTable;

struct zsvTable *zsvTable_new() {
struct zsvTable *zsvTable_new(const char *filename) {
struct zsvTable *z = sqlite3_malloc(sizeof(*z));
if(z) {
memset(z, 0, sizeof(*z));
z->parser_opts = zsv_get_default_opts();
z->custom_prop_handler = zsv_get_default_custom_prop_handler();
struct sqlite3_zsv_data *d = sqlite3_zsv_data_find(filename);
if(d) {
z->parser_opts = d->opts; // zsv_get_default_opts();
z->custom_prop_handler = d->custom_prop_handler; // zsv_get_default_custom_prop_handler();
}
}
return z;
}
Expand All @@ -120,7 +125,6 @@ typedef struct zsvCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
} zsvCursor;


/*
** The xConnect and xCreate methods do the same thing, but they must be
** different so that the virtual table is not an eponymous virtual table.
Expand Down Expand Up @@ -172,25 +176,20 @@ static int zsvtabConnect(
sqlite3_vtab **ppVtab,
char **pzErr
){
zsvTable *pNew = NULL;
(void)(_pAux);
zsvTable pTmp = { 0 };
int rc = SQLITE_OK; /* Result code from this routine */
#define ZSVTABCONNECT_PARAM_MAX 3
static const char *azParam[ZSVTABCONNECT_PARAM_MAX] = {
"filename", "options_used", "max_columns"
};
char *azPValue[ZSVTABCONNECT_PARAM_MAX]; /* Parameter values */
memset(azPValue, 0, sizeof(azPValue));
# define CSV_FILENAME (azPValue[0])
# define ZSV_OPTS_USED (azPValue[1])

char *schema = NULL;
pNew = zsvTable_new();
if(!pNew)
return SQLITE_NOMEM;

pNew->parser_opts.max_columns = 2000; /* default max columns */

(void)(_pAux);
memset(azPValue, 0, sizeof(azPValue));
zsvTable *pNew = NULL;

char *errmsg = NULL;
// set parameters
Expand All @@ -203,11 +202,11 @@ static int zsvtabConnect(
}
if( j<sizeof(azParam)/sizeof(azParam[0]) ){
if( errmsg ) goto zsvtab_connect_error;
}else
} else
// optional values
if( (zValue = csv_parameter("max_columns",11,z))!=0 ){
pNew->parser_opts.max_columns = atoi(zValue);
if(pNew->parser_opts.max_columns<=0 || pNew->parser_opts.max_columns > 2000){
pTmp.parser_opts.max_columns = atoi(zValue);
if(pTmp.parser_opts.max_columns<=0 || pTmp.parser_opts.max_columns > 2000){
asprintf(&errmsg, "max_columns= value must be > 0 and < 2000");
goto zsvtab_connect_error;
}
Expand All @@ -223,6 +222,14 @@ static int zsvtabConnect(
goto zsvtab_connect_error;
}

pNew = zsvTable_new(CSV_FILENAME);
if(!pNew)
goto zsvtab_connect_oom;
if(pTmp.parser_opts.max_columns)
pNew->parser_opts.max_columns = pTmp.parser_opts.max_columns;
else if(!pNew->parser_opts.max_columns)
pNew->parser_opts.max_columns = 2000; /* default max columns */

if(!(pNew->parser_opts.stream = fopen(CSV_FILENAME, "rb"))) {
asprintf(&errmsg, "Unable to open for reading: %s", CSV_FILENAME);
goto zsvtab_connect_error;
Expand Down Expand Up @@ -477,7 +484,9 @@ int sqlite3_csv_init(
#ifndef SQLITE_OMIT_VIRTUALTABLE
int rc;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "csv", &CsvModule, 0);
pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER;
memcpy(&sqlite3_zsv_data_mutex, &init, sizeof(init));
rc = sqlite3_create_module_v2(db, "csv", &CsvModule, &sqlite3_zsv_data_g, (void (*)(void *))sqlite3_zsv_list_delete);
#ifdef SQLITE_TEST
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "csv_wr", &CsvModuleFauxWrite, 0);
Expand All @@ -488,3 +497,4 @@ int sqlite3_csv_init(
return SQLITE_OK;
#endif
}
/* clang-format on */
Loading