From 7417ee383bc5fb69d90253414446a8b5eff79ecb Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 27 Aug 2013 08:23:02 +0200 Subject: Add support for statement processing --- common/makefile | 1 + common/statement/processing/driver.cxx | 613 +++++++++++++++++++++++++++++++++ common/statement/processing/makefile | 85 +++++ common/statement/processing/test.std | 0 4 files changed, 699 insertions(+) create mode 100644 common/statement/processing/driver.cxx create mode 100644 common/statement/processing/makefile create mode 100644 common/statement/processing/test.std diff --git a/common/makefile b/common/makefile index 3263410..1ad2389 100644 --- a/common/makefile +++ b/common/makefile @@ -46,6 +46,7 @@ schema/embedded/order \ section/basics \ section/polymorphism \ session/cache \ +statement/processing \ template \ transaction/basics \ transaction/callback \ diff --git a/common/statement/processing/driver.cxx b/common/statement/processing/driver.cxx new file mode 100644 index 0000000..34def73 --- /dev/null +++ b/common/statement/processing/driver.cxx @@ -0,0 +1,613 @@ +// file : common/statement/processing/driver.cxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// Test internal statement processing machinery. +// + +#include +#include +#include + +#include + +using namespace std; + +static bool +insert (const char* stmt, + const char* expected, + const void* const* bind, + size_t bind_size) +{ + string r; + odb::statement::process_insert ( + stmt, bind, bind_size, sizeof (void*), '$', r); + return r == expected; +} + +static bool +update (const char* stmt, + const char* expected, + const void* const* bind, + size_t bind_size) +{ + string r; + odb::statement::process_update ( + stmt, bind, bind_size, sizeof (void*), '$', r); + return r == expected; +} + +static bool +select (const char* stmt, + const char* expected, + const void* const* bind, + size_t bind_size) +{ + string r; + odb::statement::process_select ( + stmt, bind, bind_size, sizeof (void*), '[', ']', true, r); + return r == expected; +} + +int +main (int, char* argv[]) +{ + // + // INSERT + // + + // Fast path. + // + { + void* b[] = {argv, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b])\n" + "VALUES\n" + "(DEFAULT,\n$1)", + "INSERT INTO [foo] ([a], [b]) VALUES (DEFAULT, $1)", + b, 2)); + } + + // Empty via statement. + // + /* LIBODB_DEBUG_STATEMENT_PROCESSING + { + assert (insert ("INSERT INTO [foo]\n" + "DEFAULT VALUES", + "INSERT INTO [foo] DEFAULT VALUES", + 0, 0)); + } + */ + + // Empty via bind. + // + { + void* b[] = {0}; + assert (insert ("INSERT INTO [foo]\n" + "([a])\n" + "VALUES\n" + "($1)", + "INSERT INTO [foo] DEFAULT VALUES", + b, 1)); + } + + // Empty with OUTPUT. + // + { + void* b[] = {0, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b])\n" + "OUTPUT INSERTED.[id]\n" + "VALUES\n" + "($1,\n$2)", + "INSERT INTO [foo] OUTPUT INSERTED.[id] DEFAULT VALUES", + b, 2)); + } + + // Empty with RETURNING. + // + { + void* b[] = {0, 0, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$1,\n$2)\n" + "RETURNING [id]", + "INSERT INTO [foo] DEFAULT VALUES RETURNING [id]", + b, 3)); + } + + // Empty via bind, but not values. + // + { + void* b[] = {0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b])\n" + "VALUES\n" + "(1,\n$1)", + "INSERT INTO [foo] ([a]) VALUES (1)", + b, 1)); + } + + // Empty via bind, but not values. + // + { + void* b[] = {0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "(1,\n$1,\nDEFAULT)", + "INSERT INTO [foo] ([a], [c]) VALUES (1, DEFAULT)", + b, 1)); + } + + // First not present. + // + { + void* b[] = {0, argv, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([b], [c]) VALUES ($2, $3)", + b, 3)); + } + + // Last not present. + // + { + void* b[] = {argv, argv, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([a], [b]) VALUES ($1, $2)", + b, 3)); + } + + // Middle not present. + // + { + void* b[] = {argv, 0, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([a], [c]) VALUES ($1, $3)", + b, 3)); + } + + // Multiple not present. + // + { + void* b[] = {0, argv, 0, argv, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c],\n[d],\n[e])\n" + "VALUES\n" + "($1,\n$2,\n$3,\n$4,\n$5)", + "INSERT INTO [foo] ([b], [d]) VALUES ($2, $4)", + b, 5)); + } + + // Not present and OUTPUT. + // + { + void* b[] = {argv, 0, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "OUTPUT INSERTED.[id]\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([a], [c]) OUTPUT INSERTED.[id] " + "VALUES ($1, $3)", + b, 3)); + } + + // Not present and RETURNING. + // + { + void* b[] = {argv, 0, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)\n" + "RETURNING [id]", + "INSERT INTO [foo] ([a], [c]) VALUES ($1, $3) " + "RETURNING [id]", + b, 3)); + } + + // Value expressions. + // + { + void* b[] = {argv, argv, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\nCAST($2, TEXT),\n$3)", + "INSERT INTO [foo] ([a], [b], [c]) " + "VALUES ($1, CAST($2, TEXT), $3)", + b, 3)); + } + + // + // UPDATE + // + + // Fast path. + // + { + void* b[] = {argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "ver=ver+1,\n[a]=$1\n" + "WHERE [id]=$2", + "UPDATE [foo] SET ver=ver+1, [a]=$1 WHERE [id]=$2", + b, 2)); + } + + // Empty via statement. + // + { + void* b[] = {argv}; + assert (update ("UPDATE [foo]\n" + "WHERE [id]=$1", + "UPDATE [foo] WHERE [id]=$1", + b, 1)); + } + + // Empty via bind. + // + { + void* b[] = {0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1\n" + "WHERE [id]=$2", + "UPDATE [foo] WHERE [id]=$2", + b, 2)); + } + + // Empty via bind, but not values. + // + { + void* b[] = {0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "ver=ver+1,\n[a]=$1\n" + "WHERE [id]=$2", + "UPDATE [foo] SET ver=ver+1 WHERE [id]=$2", + b, 2)); + } + + // First not present. + // + { + void* b[] = {0, argv, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [b]=$2, [c]=$3 WHERE [id]=$4", + b, 4)); + } + + // Last not present. + // + { + void* b[] = {argv, argv, 0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [b]=$2 WHERE [id]=$4", + b, 4)); + } + + // Middle not present. + // + { + void* b[] = {argv, 0, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [c]=$3 WHERE [id]=$4", + b, 4)); + } + + // Multiple not present. + // + { + void* b[] = {0, argv, 0, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3,\n" + "[d]=$4\n" + "WHERE [id]=$5", + "UPDATE [foo] SET [b]=$2, [d]=$4 WHERE [id]=$5", + b, 5)); + } + + // Not present and OUTPUT. + // + { + void* b[] = {argv, 0, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "OUTPUT INSERTED.[ver] " + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [c]=$3 OUTPUT INSERTED.[ver] " + "WHERE [id]=$4", + b, 4)); + } + + // Value expressions. + // + { + void* b[] = {argv, argv, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=CAST($2, TEXT),\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [b]=CAST($2, TEXT), [c]=$3 " + "WHERE [id]=$4", + b, 4)); + } + + // No OUTPUT/WHERE clause. + // + { + void* b[] = {argv, 0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3", + "UPDATE [foo] SET [a]=$1, [c]=$3", + b, 4)); + } + + // + // SELECT + // + + // Fast path. + // + { + void* b[] = {argv, argv}; + assert (select ("SELECT\n" + "[s].[t].[x],\n" + "[a].[y]\n" + "FROM [s].[t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[t].[id]\n" + "WHERE [s].[t].[id]=$1", + "SELECT [s].[t].[x], [a].[y] FROM [s].[t] " + "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[t].[id] " + "WHERE [s].[t].[id]=$1", + b, 2)); + } + + // First not present. + // + { + void* b[] = {0, argv, argv}; + assert (select ("SELECT\n" + "[a].[x],\n" + "[t].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[y], [t].[z] FROM [t] WHERE [t].[id]=$1", + b, 3)); + } + + // Last not present. + // + { + void* b[] = {argv, argv, 0}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[t].[y],\n" + "[a].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [t].[y] FROM [t] WHERE [t].[id]=$1", + b, 3)); + } + + // Middle not present. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [t].[z] FROM [t] WHERE [t].[id]=$1", + b, 3)); + } + + // Multiple not present. + // + { + void* b[] = {0, argv, 0, argv}; + assert (select ("SELECT\n" + "[a1].[w],\n" + "[t].[x],\n" + "[a2].[y],\n" + "[a3].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a1] ON [a1].[id]=[t].[id]\n" + "LEFT JOIN [t2] AS [a2] ON [a2].[id]=[t].[id]\n" + "LEFT JOIN [t3] AS [a3] ON [a3].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [a3].[z] FROM [t] " + "LEFT JOIN [t3] AS [a3] ON [a3].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 4)); + } + + // Column expression. + // + { + void* b[] = {argv, argv, 0}; + assert (select ("SELECT\n" + "[t].[x],\n" + "CAST([a].[y], TEXT),\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], CAST([a].[y], TEXT) FROM [t] " + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 3)); + } + + // No WHERE. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[t].[y],\n" + "[t].[z]\n" + "FROM [t]", + "SELECT [t].[x], [t].[z] FROM [t]", + b, 3)); + } + + // JOIN without WHERE. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]", + "SELECT [t].[x], [t].[z] FROM [t]", + b, 3)); + } + + // JOIN presence because of WHERE. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1 AND [a].[id]=$2", + "SELECT [t].[x], [t].[z] FROM [t] " + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id] " + "WHERE [t].[id]=$1 AND [a].[id]=$2", + b, 3)); + } + + + // JOIN presence because of dependent JOIN. + // + { + void* b[] = {argv, argv, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a_b].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [d] AS [a_d] ON [a_d].[id]=[t].[id]\n" + "LEFT JOIN [b] AS [a_b] ON [a_b].[id]=[a_d].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [a_b].[y], [t].[z] FROM [t] " + "LEFT JOIN [d] AS [a_d] ON [a_d].[id]=[t].[id] " + "LEFT JOIN [b] AS [a_b] ON [a_b].[id]=[a_d].[id] " + "WHERE [t].[id]=$1", + b, 3)); + } + + // JOIN without alias and with schema. + // + { + void* b[] = {argv, argv, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[s].[t1].[y],\n" + "[t2].[z]\n" + "FROM [t]\n" + "LEFT JOIN [s].[t1] ON [s].[t1].[id]=[t].[id]\n" + "LEFT JOIN [t2] ON [t2].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [s].[t1].[y], [t2].[z] FROM [t] " + "LEFT JOIN [s].[t1] ON [s].[t1].[id]=[t].[id] " + "LEFT JOIN [t2] ON [t2].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 3)); + } + + // JOIN alias top-level qualifer test. + // + { + void* b[] = {argv, 0}; + assert (select ("SELECT\n" + "[s].[a].[x],\n" + "[a].[y]\n" + "FROM [s].[a]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[a].[id]\n" + "WHERE [s].[a].[id]=$1", + "SELECT [s].[a].[x] FROM [s].[a] WHERE [s].[a].[id]=$1", + b, 2)); + } + + // JOIN alias bottom-level qualifer test (FROM case). + // + { + void* b[] = {argv, 0}; + assert (select ("SELECT\n" + "[a].[t].[x],\n" + "[a].[y]\n" + "FROM [a].[t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[a].[t].[id]\n" + "WHERE [a].[t].[id]=$1", + "SELECT [a].[t].[x] FROM [a].[t] WHERE [a].[t].[id]=$1", + b, 2)); + } + + // JOIN alias bottom-level qualifer test (LEFT JOIN case). + // + { + void* b[] = {0, argv}; + assert (select ("SELECT\n" + "[a].[y],\n" + "[a].[t2].[x]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "LEFT JOIN [a].[t2] ON [a].[t2].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [a].[t2].[x] FROM [t] " + "LEFT JOIN [a].[t2] ON [a].[t2].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 2)); + } +} diff --git a/common/statement/processing/makefile b/common/statement/processing/makefile new file mode 100644 index 0000000..48ecf1d --- /dev/null +++ b/common/statement/processing/makefile @@ -0,0 +1,85 @@ +# file : common/statement/processing/makefile +# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +# license : GNU GPL v2; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make + +cxx_tun := driver.cxx +cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) +cxx_od := $(cxx_obj:.o=.o.d) + +common.l := $(out_root)/libcommon/common/common.l +common.l.cpp-options := $(out_root)/libcommon/common/common.l.cpp-options + +# Build. +# +$(driver): $(cxx_obj) $(common.l) +$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base) -I$(src_base) +$(cxx_obj) $(cxx_od): $(common.l.cpp-options) + +$(call include-dep,$(cxx_od)) + +# Alias for default target. +# +$(out_base)/: $(driver) + +# Dist +# +name := $(subst /,-,$(subst $(src_root)/common/,,$(src_base))) + +$(dist): sources := $(cxx_tun) +$(dist): data_dist := test.std +$(dist): export name := $(name) +$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \ +$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name)) +$(dist): + $(call dist-data,$(sources) $(headers) $(data_dist)) + $(call meta-automake,../../template/Makefile.am) + $(call meta-vc8projs,../../template/template,$(name)) + $(call meta-vc9projs,../../template/template,$(name)) + $(call meta-vc10projs,../../template/template,$(name)) + $(call meta-vc11projs,../../template/template,$(name)) + +# Test. +# +ifneq ($(db_id),common) +$(eval $(call test-schemaless-rule)) +else +$(foreach d,$(databases),$(eval $(call test-schemaless-rule,$d))) +endif + +# Clean. +# +$(clean): \ + $(driver).o.clean \ + $(addsuffix .cxx.clean,$(cxx_obj)) \ + $(addsuffix .cxx.clean,$(cxx_od)) + $(call message,,rm -f $(out_base)/test.out) + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + +# How to. +# +$(call include,$(bld_root)/dist.make) +$(call include,$(bld_root)/meta/vc8proj.make) +$(call include,$(bld_root)/meta/vc9proj.make) +$(call include,$(bld_root)/meta/vc10proj.make) +$(call include,$(bld_root)/meta/vc11proj.make) +$(call include,$(bld_root)/meta/automake.make) + +$(call include,$(bld_root)/cxx/cxx-d.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/o-e.make) + +# Dependencies. +# +$(call import,$(src_root)/libcommon/makefile) diff --git a/common/statement/processing/test.std b/common/statement/processing/test.std new file mode 100644 index 0000000..e69de29 -- cgit v1.1