Files
yasuhirokimura-db18/examples/sql/c/ex_sql_utils.c
T
2020-07-24 00:37:46 +09:00

343 lines
7.3 KiB
C

/*-
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* See the file EXAMPLES-LICENSE for license information.
*
*/
#include "ex_sql_utils.h"
/*
* This file contain the common utilities for sql examples. Includes:
* 1. Setup/Clean up Enviornment.
* 2. Output control.
* 3. Common SQL executor and error handler.
* 4. A simple multi-threaded manager.
*/
/* Setup database environment. */
db_handle *
setup(db_name)
const char* db_name;
{
db_handle *db;
/* Open database. */
sqlite3_open(db_name, &db);
error_handler(db);
return db;
}
/* Clean up the database environment. */
void
cleanup(db)
db_handle *db;
{
int rc;
/* Close database and end program. */
rc = sqlite3_close(db);
if (rc != SQLITE_OK) {
fprintf(stderr, "DB CLOSE ERROR. ERRCODE: %d.\n", rc);
exit(EXIT_FAILURE);
}
printf("DONE.\n");
}
/*
* Output Control Functions.
*/
/* Output message with line between. */
void
echo_info(info)
const char* info;
{
int i;
char ch = '=';
for (i = 0; i < 80; i++)
printf("%c", ch);
printf("\n%s\n", info);
}
/*
* Output columns for following exec_sql(). We've to use ANSI C declaration
* here to eliminate warnings in Visual Studio.
*/
static int
print_column_callback(void *data, int n_columns,
char **col_values, char **col_names)
{
int i;
printf(" "); /* Display indent. */
for (i = 0; i < n_columns; i++) {
printf("%s\t",
col_values[i] == NULL ? "" : col_values[i]);
}
printf("\n");
return 0;
}
/*
* Execute a given sql expression and print result automatically. This
* function will print error and exit if any error occurs.
*
* This function always return sqlite result code.
*/
static int
exec_sql_internal(db, sql, silent)
db_handle* db;
const char* sql;
int silent;
{
int rc; /* Result code. */
if (!silent)
printf("SQL: %s\n", sql);
/*
* Execute a sql expression. The result will be printed by
* the callback function.
*
* The 5th argument of sqlite3_exec() is errmsg buffer. In out case,
* the program does not use it and use sqlite3_errmsg() to get the most
* recent error message in error_handler(). The advantage is that we
* do not need to manage those errmsg buffers by hand. But if we share
* a connection object(sqlite3*) in multi-threads, the error message
* of one thread may be overwritten by other threads. However, sharing
* a connection object between multi-threads is not a recommended
* method in sqlite.
*/
if (!silent)
rc = sqlite3_exec(db, sql, print_column_callback, 0, NULL);
else
rc = sqlite3_exec(db, sql, NULL, 0, NULL);
error_handler(db);
return rc;
}
int
exec_sql(db, sql)
db_handle* db;
const char* sql;
{
return exec_sql_internal(db, sql, 0);
}
/*
* This is the default error handler for all examples. It always return
* the recent sqlite result code.
*
* You have to use sqlite3_extended_result_codes() instead of sqlite3_errcode()
* when extended result codes are enabled. These examples does not use extended
* code.
*/
int
error_handler(db)
db_handle *db;
{
int err_code = sqlite3_errcode(db);
switch(err_code) {
case SQLITE_OK:
case SQLITE_DONE:
case SQLITE_ROW:
/* Do nothing. */
break;
default:
fprintf(stderr, "ERROR: %s. ERRCODE: %d.\n",
sqlite3_errmsg(db), err_code);
exit(err_code);
}
return err_code;
}
/*
* Pre-load database, create a table and insert rows by given a csv file.
*/
#define BUF_SIZE 1024
#define TABLE_COLS 16
#define SQL_COMMAND_SIZE 4096
/*
* Here is the definition of the university sample table. As we can see from
* below, the university table has 9 columns with types including int and
* varchar.
*/
const sample_data university_sample_data = {
"university",
"\tDROP TABLE IF EXISTS university;\n"
"\tCREATE TABLE university\n"
"\t(\n"
"\t\trank int,\n"
"\t\tname varchar(75),\n"
"\t\tdomains varchar(75),\n"
"\t\tcountry varchar(30),\n"
"\t\tregion varchar(30),\n"
"\t\tsize int,\n"
"\t\tvisibility int,\n"
"\t\trich int,\n"
"\t\tscholar int\n"
"\t);",
"../examples/sql/data/university.csv", 9};
/*
* Here is the definition of the country sample table. As we can see from
* below, the country table has 6 columns with types including int and
* varchar.
*/
const sample_data country_sample_data = {
"country",
"\tDROP TABLE IF EXISTS country;\n"
"\tCREATE TABLE country\n"
"\t(\n"
"\tcountry varchar(30),\n"
"\tabbr varchar(10),\n"
"\tTop_100 int,\n"
"\tTop_200 int,\n"
"\tTop_500 int,\n"
"\tTop_1000 int\n"
"\t);\n",
"../examples/sql/data/country.csv", 6};
static char items[TABLE_COLS][BUF_SIZE];
/* Open the sample csv file handle. */
static FILE*
open_csv_file(data_source)
const char* data_source;
{
FILE* fp;
fp = fopen(data_source, "r");
if (fp == NULL) {
fprintf(stderr, "%s open error.", data_source);
exit(EXIT_FAILURE);
}
return fp;
}
/* Get data line-by-line and insert it into the database. */
static int
iterate_csv_file(fp, n_cols)
FILE* fp;
int n_cols;
{
static char file_line[BUF_SIZE * TABLE_COLS];
const char delims[] = ",";
char *result;
int i;
/* Skip header row. */
if (ftell(fp) == 0) {
fgets(file_line, sizeof(file_line), fp);
}
/* Get one line. */
if (fgets(file_line, sizeof(file_line), fp) == NULL) {
fclose(fp);
return 0;
}
/* Token sentence by delimiters "," */
result = strtok(file_line, delims);
for (i = 0; result != NULL && i < n_cols; i++) {
strcpy(items[i], result);
result = strtok(NULL, delims);
}
return 1;
}
/* Common utility: load given csv file into database. */
void
load_table_from_file(db, data, silent)
sqlite3* db;
sample_data data;
int silent;
{
FILE *fp;
int i, n;
char buf[SQL_COMMAND_SIZE];
sprintf(buf, "Load data source %s into database.",
data.source_file);
echo_info(buf);
/* Create table by given SQL expression. */
exec_sql_internal(db, data.sql_create, silent);
fp = open_csv_file(data.source_file);
while (iterate_csv_file(fp, data.ncolumn) != 0) {
/* Get data line by line and put it into database. */
i = sprintf(buf, "INSERT INTO %s VALUES(", data.table_name);
if (data.ncolumn > 0 ) {
for (n = 0; n < data.ncolumn; n++) {
i += sprintf(buf + i, "'%s',", items[n]);
}
/* -3 to delete last "\n'," */
sprintf(buf + i - 3, "');");
exec_sql_internal(db, buf, silent);
}
}
printf("Load done.\n");
}
/*
* A very simple multi-threaded manager for concurrent examples.
*/
static os_thread_t thread_stack[MAX_THREAD];
static int pstack = 0;
/* All created thread ids will be pushed into stack for managing. */
void
register_thread_id(pid)
os_thread_t pid;
{
/* Push pid into stack. */
if (pstack < MAX_THREAD) {
thread_stack[pstack++] = pid;
} else {
fprintf(stderr, "Error: Too many threads!\n");
}
}
/* Join for all threads in stack when finished. */
int
join_threads()
{
int i;
int status = 0;
#if defined(WIN32)
#else
void *retp;
#endif
for (i = 0; i < pstack ; i++) {
#if defined(WIN32)
if (WaitForSingleObject(thread_stack[i], INFINITE) == WAIT_FAILED) {
status = 1;
printf("join_threads: child %ld exited with error %s\n",
(long)i, strerror(GetLastError()));
}
#else
pthread_join(thread_stack[i], &retp);
if (retp != NULL) {
status = 1;
printf("join_threads: child %ld exited with error\n",
(long)i);
}
#endif
}
pstack = 0;
return status;
}