text: #include <stdio.h>
  #include <stdlib.h>
  #include <sqlite3.h>
  #include <string.h>
  #include <math.h>
  
  int split(char *string, char sep, int n_char, char ***array_values)
  {
  	int n_values = 1;
  	int start = 0;
  
  	for(int x = 0; x < n_char; x++)
  	{
  		if(string[x] == sep)
  			n_values++;
  	}
  	*array_values = malloc(n_values * sizeof(char**));
  
  	for(int x = 0; x < n_values; x++)
  	{
  		for(int y = start; y <= n_char; y++)
  		{
  			if(string[y] == sep || string[y] == '\0')
  			{
  				(*array_values)[x] = malloc((y - start + 1) * sizeof(char*));
  				for(int z = 0; z < y - start; z++)
  					(*array_values)[x][z] = string[z + start];
  				(*array_values)[x][y - start] = '\0';
  				start = y + 1;
  				break;
  			}
  		}
  	}
  
  	return n_values;
  }
  
  void free_split(char ***array_values, int n_values)
  {
  	for(int x = 0; x < n_values; x++)
  	{
  		free((*array_values)[x]);
  	}
  	free(*array_values);
  }
  
  // This function is used to dispose of the string generated by a binding
  void deconstructor(void *string)
  {
  	/* I don't know why but it seems like freeing memory here gives a double mem free error,
  	 * I think this is due to the fact that the same pointer that is given to deconstructor will
  	 * be freed by free_split(). This is speculation and might not be true.
  	 * This should be treated as unexpected behaviour
  	 */
  }
  
  double distance(double x1, double y1, double z1, double x2, double y2, double z2)
  {
  	double x, y, z;
  	double ris;
  	x = x1 - x2;
  	y = y1 - y2;
  	z = z1 - z2;
  	ris = sqrt(x * x + y * y);
  	ris = sqrt(ris * ris + z * z);
  	return ris;
  }
  
  int main()
  {
  	char *db_name = "map.db";
  	char *data_source = "systems.csv";
  	sqlite3 *conn;
  	sqlite3_stmt *statement;
  	sqlite3_stmt *statement2;
  	sqlite3_stmt *insert_statement;
  	char *errMsg;
  	char *query;
  	int status;
  	FILE *fd;
  	char *buffer;
  	size_t buffer_len = 0;
  	char **values;
  	int n_values;
  
  	const int JUMP_DISTANCE = 20;
  
  	status = sqlite3_open(db_name, &conn);
  	if(status == SQLITE_ERROR)
  	{
  		printf("Error: %s\n", sqlite3_errmsg(conn));
  		return 1;
  	}
  
  	query = "CREATE TABLE systems (\
  		 id INT NOT NULL PRIMARY KEY,\
  		 name TEXT NOT NULL,\
  		 x FLOAT NOT NULL,\
  		 y FLOAT NOT NULL,\
  		 z FLOAT NOT NULL)";
  
  	// Remember to destroy prepared statements to avoid mem leaks
  	status = sqlite3_prepare(conn, query, -1, &statement, NULL);
  	if(status == SQLITE_ERROR)
  	{
  		printf("Error: %s\n", sqlite3_errmsg(conn));
  		return 1;
  	}
  
  	status = sqlite3_step(statement);
  	if(status == SQLITE_ERROR)
  	{
  		printf("Error: %s\n", sqlite3_errmsg(conn));
  		return 1;
  	}
  
  	// This function is used to destroy a prepared statement
  	status = sqlite3_finalize(statement);
  	if(status == SQLITE_ERROR)
  	{
  		printf("Error: %s\n", sqlite3_errmsg(conn));
  		return 1;
  	}
  
  	printf("Parsing %s...", data_source);
  	fflush(stdout);
  	fd = fopen(data_source, "r");
  	if(fd == NULL)
  	{
  		printf("Unable to open data_source file\n");
  		return 2;
  	}
  
  	// Transactions for bulk inserts are not automatically handled
  	// by the sqlite3 API
  	sqlite3_exec(conn, "BEGIN TRANSACTION", NULL, NULL, &errMsg);
  	if(errMsg != NULL)
  	{
  		printf("%s\n", errMsg);
  		sqlite3_free(errMsg);
  		return 1;
  	}
  
  	query = "INSERT INTO systems(id, name, x, y, z) VALUES (?, ?, ?, ?, ?)";
  	status = sqlite3_prepare(conn, query, -1, &statement, NULL);
  
  	while(getline(&buffer, &buffer_len, fd) != -1)
  	{
  		n_values = split(buffer, ',', strlen(buffer), &values);
  		sqlite3_bind_int(statement, 1, atoi(values[0]));
  		sqlite3_bind_text(statement, 2, values[2], -1, NULL); // Using NULL instead of deconstructor
  		sqlite3_bind_double(statement, 3, atof(values[3]));
  		sqlite3_bind_double(statement, 4, atof(values[4]));
  		sqlite3_bind_double(statement, 5, atof(values[5]));
  
  		sqlite3_step(statement);
  		sqlite3_reset(statement);
  
  		free_split(&values, n_values);
  	}
  	/* as https://stackoverflow.com/questions/42478868/how-do-i-properly-free-memory-related-to-getline-function
  	 * points out, if we reuse the same pointer we'll only need to free memory once */
  	free(buffer);
  	sqlite3_exec(conn, "COMMIT TRANSACTION", NULL, NULL, &errMsg);
  	if(errMsg != NULL)
  	{
  		printf("%s\n", errMsg);
  		sqlite3_free(errMsg);
  		return 1;
  	}
  	printf("done\n");
  	fclose(fd);
  
  	status = sqlite3_finalize(statement);
  	if(status == SQLITE_ERROR)
  	{
  		printf("Error: %s\n", sqlite3_errmsg(conn));
  		return 1;
  	}
  
  	sqlite3_close(conn);
  }
  