From 8d050f3c30f3b97f5c0c33b91bb1216b06b7688c Mon Sep 17 00:00:00 2001 From: anon Date: Tue, 13 Feb 2024 19:46:34 +0100 Subject: [PATCH] init --- .gitignore | 7 + Makefile | 48 +++ README.md | 62 +++ debug/static_insert.sqc | 8 + ....17._Internals (9_24_2023 8_47_03 PM).html | 392 ++++++++++++++++++ object/.gitkeep | 0 peru.yaml | 5 + source/Database.hpp | 5 + source/esql.l | 57 +++ source/main.cpp | 72 ++++ source/sqlite.l | 23 + source/sqlite.y | 47 +++ 12 files changed, 726 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 debug/static_insert.sqc create mode 100644 documentation/PostgreSQL_ Documentation_ 16_ 36.17._Internals (9_24_2023 8_47_03 PM).html create mode 100644 object/.gitkeep create mode 100644 peru.yaml create mode 100644 source/Database.hpp create mode 100644 source/esql.l create mode 100644 source/main.cpp create mode 100644 source/sqlite.l create mode 100644 source/sqlite.y diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de47703 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +esql +object/ +*.out +.gdb_history +venv/ + +.peru diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9f6565e --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3c9749 --- /dev/null +++ b/README.md @@ -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 + +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 diff --git a/debug/static_insert.sqc b/debug/static_insert.sqc new file mode 100644 index 0000000..d67014f --- /dev/null +++ b/debug/static_insert.sqc @@ -0,0 +1,8 @@ +#include + +signed main(){ + EXEC SQL CONNECT TO testdb AS myconnection; + EXEC SQL INSERT INTO a VALUES (10); + + return i; +} diff --git a/documentation/PostgreSQL_ Documentation_ 16_ 36.17._Internals (9_24_2023 8_47_03 PM).html b/documentation/PostgreSQL_ Documentation_ 16_ 36.17._Internals (9_24_2023 8_47_03 PM).html new file mode 100644 index 0000000..a075ace --- /dev/null +++ b/documentation/PostgreSQL_ Documentation_ 16_ 36.17._Internals (9_24_2023 8_47_03 PM).html @@ -0,0 +1,392 @@ + +PostgreSQL: Documentation: 16: 36.17. Internals + + + + + + + + + + + + + +
+
+
+ + +
+
+
+
14th September 2023: + PostgreSQL 16 Released! + +
+
+
+
+
+
+
+
+
+ +
+ +
+
+ Supported Versions: + + + + Current + (16) + + + / + + 15 + + + / + + 14 + + + / + + 13 + + + / + + 12 + + + / + + 11 + + +
+
+ + +
+
+ Development Versions: + + + devel + +
+
+ + +
+
+ Unsupported versions: + + + 10 + + / + 9.6 + + / + 9.5 + + / + 9.4 + + / + 9.3 + + / + 9.2 + + / + 9.1 + + / + 9.0 + + / + 8.4 + + / + 8.3 + + / + 8.2 + + / + 8.1 + + / + 8.0 + + / + 7.4 + + / + 7.3 + + / + 7.2 + + / + 7.1 + +
+
+ +
+
+ +
+
+
+ +
+
+
+
+

36.17. Internals #

+
+
+
+

This section explains how ECPG works internally. This information can occasionally be useful to help users understand how to use ECPG.

+

The first four lines written by ecpg to the output are fixed lines. Two are comments and two are include lines necessary to interface to the library. Then the preprocessor reads through the file and writes output. Normally it just echoes everything to the output.

+

When it sees an EXEC SQL statement, it intervenes and changes it. The command starts with EXEC SQL and ends with ;. Everything in between is treated as an SQL statement and parsed for variable substitution.

+

Variable substitution occurs when a symbol starts with a colon (:). The variable with that name is looked up among the variables that were previously declared within a EXEC SQL DECLARE section.

+

The most important function in the library is ECPGdo, which takes care of executing most commands. It takes a variable number of arguments. This can easily add up to 50 or so arguments, and we hope this will not be a problem on any platform.

+

The arguments are:

+
+
+
A line number #
+
+

This is the line number of the original line; used in error messages only.

+
+
A string #
+
+

This is the SQL command that is to be issued. It is modified by the input variables, i.e., the variables that where not known at compile time but are to be entered in the command. Where the variables should go the string contains ?.

+
+
Input variables #
+
+

Every input variable causes ten arguments to be created. (See below.)

+
+
ECPGt_EOIT #
+
+

An enum telling that there are no more input variables.

+
+
Output variables #
+
+

Every output variable causes ten arguments to be created. (See below.) These variables are filled by the function.

+
+
ECPGt_EORT #
+
+

An enum telling that there are no more variables.

+
+
+
+

For every variable that is part of the SQL command, the function gets ten arguments:

+
+
    +
  1. +

    The type as a special symbol.

    +
  2. +
  3. +

    A pointer to the value or a pointer to the pointer.

    +
  4. +
  5. +

    The size of the variable if it is a char or varchar.

    +
  6. +
  7. +

    The number of elements in the array (for array fetches).

    +
  8. +
  9. +

    The offset to the next element in the array (for array fetches).

    +
  10. +
  11. +

    The type of the indicator variable as a special symbol.

    +
  12. +
  13. +

    A pointer to the indicator variable.

    +
  14. +
  15. +

    0

    +
  16. +
  17. +

    The number of elements in the indicator array (for array fetches).

    +
  18. +
  19. +

    The offset to the next element in the indicator array (for array fetches).

    +
  20. +
+
+

Note that not all SQL commands are treated in this way. For instance, an open cursor statement like:

+
EXEC SQL OPEN cursor;
+
+

is not copied to the output. Instead, the cursor's DECLARE command is used at the position of the OPEN command because it indeed opens the cursor.

+

Here is a complete example describing the output of the preprocessor of a file foo.pgc (details might change with each particular version of the preprocessor):

+
EXEC SQL BEGIN DECLARE SECTION;
+int index;
+int result;
+EXEC SQL END DECLARE SECTION;
+...
+EXEC SQL SELECT res INTO :result FROM mytable WHERE index = :index;
+
+

is translated into:

+
/* Processed by ecpg (2.6.0) */
+/* These two include files are added by the preprocessor */
+#include <ecpgtype.h>;
+#include <ecpglib.h>;
+
+/* exec sql begin declare section */
+
+#line 1 "foo.pgc"
+
+ int index;
+ int result;
+/* exec sql end declare section */
+...
+ECPGdo(__LINE__, NULL, "SELECT res FROM mytable WHERE index = ?     ",
+        ECPGt_int,&(index),1L,1L,sizeof(int),
+        ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,
+        ECPGt_int,&(result),1L,1L,sizeof(int),
+        ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 147 "foo.pgc"
+
+
+

(The indentation here is added for readability and not something the preprocessor does.)

+
+ +
+ +
+

Submit correction

+

+ If you see anything in the documentation that is not correct, does not match + your experience with the particular feature or requires further clarification, + please use + this form + to report a documentation issue. +

+
+ +
+
+
+ + + + + + + diff --git a/object/.gitkeep b/object/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/peru.yaml b/peru.yaml new file mode 100644 index 0000000..5ebaf4e --- /dev/null +++ b/peru.yaml @@ -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 diff --git a/source/Database.hpp b/source/Database.hpp new file mode 100644 index 0000000..5e08416 --- /dev/null +++ b/source/Database.hpp @@ -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; +}; diff --git a/source/esql.l b/source/esql.l new file mode 100644 index 0000000..dc7ea7c --- /dev/null +++ b/source/esql.l @@ -0,0 +1,57 @@ +%{ + +#include +#include + +#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] + +%% + +{ + +EXEC{wsnl}+SQL{wsnl}+ { + BEGIN SQL; +} + +. { + ; +} + +\n { + ++yylineno; +} + +} + +{ +; { + sql += yytext; + printf("Found SQL: \"%s\"\n", sql.c_str()); + db->eval(sql.c_str()); + sql = ""; + BEGIN INITIAL; +} + +. { + sql += yytext; +} + +} + +%% diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..0bcd210 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,72 @@ +#include +#include + +#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; +} diff --git a/source/sqlite.l b/source/sqlite.l new file mode 100644 index 0000000..63a686c --- /dev/null +++ b/source/sqlite.l @@ -0,0 +1,23 @@ +%{ + +#include "sqlite.tab.h" + +%} + +%option noyywrap +%option nodefault + +%% + +{ +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; } +} + +%% diff --git a/source/sqlite.y b/source/sqlite.y new file mode 100644 index 0000000..d091b81 --- /dev/null +++ b/source/sqlite.y @@ -0,0 +1,47 @@ +%{ +#include +#include + +#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 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); +}