mirror of
https://github.com/yasuhirokimura/db18
synced 2026-06-08 18:29:23 +00:00
343 lines
7.3 KiB
C
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;
|
|
}
|
|
|