This commit is contained in:
anon 2024-02-13 19:46:34 +01:00
commit 8d050f3c30
12 changed files with 726 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
esql
object/
*.out
.gdb_history
venv/
.peru

48
Makefile Normal file
View File

@ -0,0 +1,48 @@
ifeq (${DEBUG}, 1)
LFLAGS += --debug --trace
CFLAGS += -Wall -Wextra -Wpedantic
CFLAGS += -DDEBUG -O0 -ggdb -fno-inline
CXXFLAGS += -Wall -Wextra -Wpedantic
CXXFLAGS += -DDEBUG -O0 -ggdb -fno-inline
WRAP := valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all
else
CXXFLAGS += -O3 -fno-stack-protector -fno-exceptions -fno-rtti
endif
CXXFLAGS += -Isource/ -Iobject/ -Isource/sqlfun/source/
LDLIBS += $$(pkg-config --cflags --libs sqlite3 jansson)
GENSOURCE := object/sqlite.tab.cpp object/sqlite.yy.cpp object/esql.yy.cpp
GENOBJECT := $(subst .cpp,.o,${GENSOURCE})
REEEEEEEE := exec.o sql.tab.o lib.o sql.o
REEEEEEEE := $(addprefix source/sqlfun/source/,${REEEEEEEE})
OUTPUT := esql
object/%.yy.cpp: source/%.l
flex ${LFLAGS} --prefix=$(basename $(notdir $<))_ --header-file=object/$(basename $(notdir $<)).yy.h -o $@ $<
object/%.tab.cpp: source/%.y
bison --name-prefix=$(basename $(notdir $<))_ --header=object/$(basename $(notdir $<)).tab.h -o $@ $<
object/%.o: source/%.c
${COMPILE.c} $< -o $@
object/%.o: source/%.cpp
${COMPILE.cpp} $< -o $@
object/%.o: object/%.c
${COMPILE.c} $< -o $@
object/%.o: object/%.cpp
${COMPILE.cpp} $< -o $@
main: ${GENOBJECT} source/main.o ${REEEEEEEE}
${LINK.cpp} -o ${OUTPUT} $+ ${LDLIBS}
clean:
-rm object/*
-cd source/sqlfun/; make clean

62
README.md Normal file
View File

@ -0,0 +1,62 @@
# Embeded SQL
> Framework and SQLite implementation of DB2 style embeded SQl for C/C++
## What is embeded SQL?
In short, this:
```
#include <stdio.h>
signed main(){
EXEC SQL BEGIN DECLARE SECTION;
int i;
char c[4];
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO testdb AS myconnection;
EXEC SQL SELECT i, text INTO :i, :c FROM test WHERE i = 1;
puts(c);
return i;
}
```
It, in broad sense, is what PL/SQL is for Ada, for C/C++.
With an extra step of preprocessing,
the above is turned into conventional C,
that any compiler will be able to deal with.
## Why?
Embeded SQL provides the following benefits:
+ better syntax highlighting compatibility
+ compile time SQL syntax checks
+ more agile database <-> language type conversions
## Who is responsible for this?
IBM is.
The embeded SQL debuted with their DB2.
I am awfully unsure,
which edition,
but I believe it originates from the 80s.
In 1998,
Michael Meskes started Postgresqls on going embeded SQL support
with the following comment:
> Well this is not really a patch.
But I mananged to get Linus' old Postgres95 precompiler
to compile and work with PostgreSQL.
What that implies is under investigation.
Regardles the standard Postgresql tool `ecpg`
is highly functional
and something I can only recommend for people
working with Postgresql in specific.
However,
I find the usage of databases with a server architecture
for non-enterprise purposes of rather poor taste.
This is why this project sets out to
start supporting SQLite/embeded SQL and
to create a system where adding arbitrary databases is easy

8
debug/static_insert.sqc Normal file
View File

@ -0,0 +1,8 @@
#include <stdio.h>
signed main(){
EXEC SQL CONNECT TO testdb AS myconnection;
EXEC SQL INSERT INTO a VALUES (10);
return i;
}

File diff suppressed because one or more lines are too long

0
object/.gitkeep Normal file
View File

5
peru.yaml Normal file
View File

@ -0,0 +1,5 @@
imports:
sqlfun: source/sqlfun # This is where we want peru to put the module.
git module sqlfun:
url: http://github.com/agvxov/sqlfun.git

5
source/Database.hpp Normal file
View File

@ -0,0 +1,5 @@
class Database {
public:
virtual char * connect(const char * const to, const char * const as) = 0;
virtual char * eval(const char * const sql) = 0;
};

57
source/esql.l Normal file
View File

@ -0,0 +1,57 @@
%{
#include <stdio.h>
#include <string>
#include "Database.hpp"
using namespace std;
extern Database * db;
string sql = "";
%}
%option noyywrap
%option nodefault
%x SQL
ws [ \t\r\v\f]
wsnl [ \t\r\v\f\n]
%%
<INITIAL>{
EXEC{wsnl}+SQL{wsnl}+ {
BEGIN SQL;
}
. {
;
}
\n {
++yylineno;
}
}
<SQL>{
; {
sql += yytext;
printf("Found SQL: \"%s\"\n", sql.c_str());
db->eval(sql.c_str());
sql = "";
BEGIN INITIAL;
}
. {
sql += yytext;
}
}
%%

72
source/main.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <stdio.h>
#include <sqlite3.h>
#include "esql.yy.h"
#include "sqlite.yy.h"
extern "C" {
#include "sql.tab.h"
#include "yyl.h"
#include "sql.lex.h"
#include "sql-parser.h"
}
#include "Database.hpp"
#include "sqlite.tab.h"
class Sqlite : public Database {
struct psql_state *pstate;
int validate(const char * const sql) {
int r;
char * dup = strdup(sql);
psql_set_string_input(pstate, dup);
r = psql_parse(pstate);
free(dup);
return !r
&& !yyerrno;
}
char * connect(const char * const to, const char * const as) override {
char * r = NULL;
#if DEBUG
printf("Connecting to \"%s\" as \"%s\"\n", to, as);
#endif
return r;
}
char * eval(const char * const sql) override {
char * r = NULL;
int v = validate(sql);
#if DEBUG
printf("Recieved SQL: \"%s\" (%d)\n", sql, v);
#endif
return r;
}
public:
Sqlite() {
pstate = psql_new();
if (!pstate) {
throw 1;
}
}
~Sqlite() {
psql_free(pstate);
}
};
Database * db;
signed main(int argc, char * * argv) {
esql_in = fopen(argv[1], "r");
if (!esql_in) {
exit(1);
}
Sqlite sqlite;
db = &sqlite;
esql_lex();
return 0;
}

23
source/sqlite.l Normal file
View File

@ -0,0 +1,23 @@
%{
#include "sqlite.tab.h"
%}
%option noyywrap
%option nodefault
%%
<INITIAL>{
CONNECT\ TO { return CONNECT; }
AS { return AS; }
[a-zA-Z]+ { sqlite_lval.strval = strdup(yytext); return STRING; }
\( { sqlite_lval.strval = strdup(yytext); return STRING; }
\) { sqlite_lval.strval = strdup(yytext); return STRING; }
[0-9]+ { sqlite_lval.strval = strdup(yytext); return STRING; }
[ \t\n] { ; }
; { return END; }
}
%%

47
source/sqlite.y Normal file
View File

@ -0,0 +1,47 @@
%{
#include <stdio.h>
#include <string>
#include "Database.hpp"
extern Database * db;
extern int sqlite_lex();
extern int sqlite_parse();
extern FILE* sqlite_in;
void sqlite_error(const char *s);
using namespace std;
string statement_buffer = "";
%}
%union {
int intval;
char* strval;
}
%token CONNECT AS
%token <strval> STRING
%token END
%%
sql: connect
| statement
;
connect: CONNECT STRING AS STRING END { db->connect($2, $4); }
;
statement: STRING { statement_buffer += $1; }
| statement STRING { statement_buffer += $2; }
| statement END { db->eval(statement_buffer.c_str()); statement_buffer = ""; }
;
%%
void sqlite_error(const char *s) {
fprintf(stderr, "Error: %s\n", s);
}