From 7daabdddd9ba972fa26b7a44d733090f7a5afee2 Mon Sep 17 00:00:00 2001 From: elong Date: Thu, 1 Oct 2015 11:47:35 -0400 Subject: [PATCH] add platform, dialect, and dml statement --- .../Debug/src/util/subdir.mk | 6 + .../Debug/symclient_test | Bin 180844 -> 210125 bytes .../inc/symclient_test.h | 2 + .../run/symclient_test.launch | 2 +- .../src/symclient_test.c | 5 +- .../src/util/ListTest.c | 121 +++++++++++ symmetric-client-clib-test/src/util/MapTest.c | 104 ++++++++++ .../src/util/StringBuilderTest.c | 6 +- symmetric-client-clib/Debug/makefile | 4 + symmetric-client-clib/Debug/sources.mk | 4 + .../Debug/src/db/model/subdir.mk | 3 + .../Debug/src/db/platform/sqlite/subdir.mk | 33 +++ .../Debug/src/db/platform/subdir.mk | 30 +++ .../Debug/src/db/sql/subdir.mk | 27 +++ .../Debug/src/db/sqlite/subdir.mk | 24 +++ symmetric-client-clib/Debug/src/db/subdir.mk | 12 -- .../Debug/src/io/data/subdir.mk | 9 +- .../Debug/src/util/subdir.mk | 12 +- symmetric-client-clib/inc/core/SymEngine.h | 4 +- symmetric-client-clib/inc/db/SymDialect.h | 2 +- .../inc/db/SymDialectFactory.h | 2 +- symmetric-client-clib/inc/db/model/Column.h | 40 ++++ symmetric-client-clib/inc/db/model/Table.h | 25 ++- .../inc/db/platform/DatabaseInfo.h | 33 +++ .../inc/db/{ => platform}/DatabasePlatform.h | 24 ++- .../{ => platform}/DatabasePlatformFactory.h | 4 +- .../inc/db/platform/DdlReader.h | 35 ++++ .../inc/db/platform/sqlite/SqliteDdlReader.h | 38 ++++ .../db/{ => platform/sqlite}/SqlitePlatform.h | 5 +- .../inc/db/sql/DmlStatement.h | 47 +++++ symmetric-client-clib/inc/db/sql/Row.h | 46 +++++ .../inc/db/sql/SqlTemplate.h | 62 ++++++ .../inc/db/sql/SqlTransaction.h | 44 ++++ .../inc/db/{ => sqlite}/SqliteDialect.h | 0 .../inc/db/sqlite/SqliteSqlTemplate.h | 41 ++++ .../inc/db/sqlite/SqliteSqlTransaction.h | 43 ++++ symmetric-client-clib/inc/io/data/Batch.h | 2 +- symmetric-client-clib/inc/io/data/CsvData.h | 16 +- .../inc/io/reader/ProtocolDataReader.h | 13 +- .../inc/io/writer/DataWriter.h | 4 +- .../inc/io/writer/DefaultDatabaseWriter.h | 14 +- symmetric-client-clib/inc/libsymclient.h | 5 +- symmetric-client-clib/inc/model/Node.h | 2 + .../inc/service/DataLoaderService.h | 2 +- symmetric-client-clib/inc/util/List.h | 60 ++++++ symmetric-client-clib/inc/util/Map.h | 28 +++ symmetric-client-clib/inc/util/Properties.h | 2 +- .../util/{ArrayBuilder.h => StringArray.h} | 40 ++-- .../inc/util/StringBuilder.h | 11 +- .../db/{DatabasePlatform.c => model/Column.c} | 36 ++-- symmetric-client-clib/src/db/model/Table.c | 107 +++++++++- .../src/db/platform/DatabasePlatform.c | 49 +++++ .../{ => platform}/DatabasePlatformFactory.c | 2 +- .../src/db/platform/DdlReader.c | 31 +++ .../src/db/platform/sqlite/SqliteDdlReader.c | 96 +++++++++ .../db/{ => platform/sqlite}/SqlitePlatform.c | 18 +- .../db/platform/sqlite/SqliteSqlTemplate.c | 188 ++++++++++++++++++ .../db/platform/sqlite/SqliteSqlTransaction.c | 128 ++++++++++++ .../src/db/sql/DmlStatement.c | 178 +++++++++++++++++ symmetric-client-clib/src/db/sql/Row.c | 95 +++++++++ .../src/db/{ => sqlite}/SqliteDialect.c | 6 +- symmetric-client-clib/src/io/data/Batch.c | 6 - symmetric-client-clib/src/io/data/CsvData.c | 31 +-- .../src/io/reader/ProtocolDataReader.c | 121 +++++------ .../src/io/writer/DefaultDatabaseWriter.c | 93 ++++++++- symmetric-client-clib/src/model/Node.c | 1 + .../src/service/DataLoaderService.c | 2 +- .../src/service/PushService.c | 3 +- .../src/transport/http/HttpTransportManager.c | 4 +- symmetric-client-clib/src/util/ArrayBuilder.c | 145 -------------- symmetric-client-clib/src/util/List.c | 119 +++++++++++ symmetric-client-clib/src/util/Map.c | 151 ++++++++++++++ symmetric-client-clib/src/util/Properties.c | 21 +- symmetric-client-clib/src/util/StringArray.c | 113 +++++++++++ .../src/util/StringBuilder.c | 34 ++-- 75 files changed, 2470 insertions(+), 406 deletions(-) create mode 100644 symmetric-client-clib-test/src/util/ListTest.c create mode 100644 symmetric-client-clib-test/src/util/MapTest.c create mode 100644 symmetric-client-clib/Debug/src/db/platform/sqlite/subdir.mk create mode 100644 symmetric-client-clib/Debug/src/db/platform/subdir.mk create mode 100644 symmetric-client-clib/Debug/src/db/sql/subdir.mk create mode 100644 symmetric-client-clib/Debug/src/db/sqlite/subdir.mk create mode 100644 symmetric-client-clib/inc/db/model/Column.h create mode 100644 symmetric-client-clib/inc/db/platform/DatabaseInfo.h rename symmetric-client-clib/inc/db/{ => platform}/DatabasePlatform.h (65%) rename symmetric-client-clib/inc/db/{ => platform}/DatabasePlatformFactory.h (92%) create mode 100644 symmetric-client-clib/inc/db/platform/DdlReader.h create mode 100644 symmetric-client-clib/inc/db/platform/sqlite/SqliteDdlReader.h rename symmetric-client-clib/inc/db/{ => platform/sqlite}/SqlitePlatform.h (87%) create mode 100644 symmetric-client-clib/inc/db/sql/DmlStatement.h create mode 100644 symmetric-client-clib/inc/db/sql/Row.h create mode 100644 symmetric-client-clib/inc/db/sql/SqlTemplate.h create mode 100644 symmetric-client-clib/inc/db/sql/SqlTransaction.h rename symmetric-client-clib/inc/db/{ => sqlite}/SqliteDialect.h (100%) create mode 100644 symmetric-client-clib/inc/db/sqlite/SqliteSqlTemplate.h create mode 100644 symmetric-client-clib/inc/db/sqlite/SqliteSqlTransaction.h create mode 100644 symmetric-client-clib/inc/util/List.h create mode 100644 symmetric-client-clib/inc/util/Map.h rename symmetric-client-clib/inc/util/{ArrayBuilder.h => StringArray.h} (59%) rename symmetric-client-clib/src/db/{DatabasePlatform.c => model/Column.c} (53%) create mode 100644 symmetric-client-clib/src/db/platform/DatabasePlatform.c rename symmetric-client-clib/src/db/{ => platform}/DatabasePlatformFactory.c (96%) create mode 100644 symmetric-client-clib/src/db/platform/DdlReader.c create mode 100644 symmetric-client-clib/src/db/platform/sqlite/SqliteDdlReader.c rename symmetric-client-clib/src/db/{ => platform/sqlite}/SqlitePlatform.c (80%) create mode 100644 symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTemplate.c create mode 100644 symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTransaction.c create mode 100644 symmetric-client-clib/src/db/sql/DmlStatement.c create mode 100644 symmetric-client-clib/src/db/sql/Row.c rename symmetric-client-clib/src/db/{ => sqlite}/SqliteDialect.c (95%) delete mode 100644 symmetric-client-clib/src/util/ArrayBuilder.c create mode 100644 symmetric-client-clib/src/util/List.c create mode 100644 symmetric-client-clib/src/util/Map.c create mode 100644 symmetric-client-clib/src/util/StringArray.c diff --git a/symmetric-client-clib-test/Debug/src/util/subdir.mk b/symmetric-client-clib-test/Debug/src/util/subdir.mk index 16192e26fd..437ac13b0b 100644 --- a/symmetric-client-clib-test/Debug/src/util/subdir.mk +++ b/symmetric-client-clib-test/Debug/src/util/subdir.mk @@ -4,14 +4,20 @@ # Add inputs and outputs from these tool invocations to the build variables C_SRCS += \ +../src/util/ListTest.c \ +../src/util/MapTest.c \ ../src/util/PropertiesTest.c \ ../src/util/StringBuilderTest.c OBJS += \ +./src/util/ListTest.o \ +./src/util/MapTest.o \ ./src/util/PropertiesTest.o \ ./src/util/StringBuilderTest.o C_DEPS += \ +./src/util/ListTest.d \ +./src/util/MapTest.d \ ./src/util/PropertiesTest.d \ ./src/util/StringBuilderTest.d diff --git a/symmetric-client-clib-test/Debug/symclient_test b/symmetric-client-clib-test/Debug/symclient_test index cd2cfa61819dbebab0bf74db1ee5f37b28908718..70861c0e263e364d211741e57d392c8f8a2f02a7 100755 GIT binary patch delta 71695 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 KmKrg6^ke&4Vxaht?CaMJWDbRxWc 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 P(?P{6?TXlaQsMst*KK{n delta 50531 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 z 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 aU}-Q}aM{{2QZzD4w+f=KWkvi+>3!`lu3 diff --git a/symmetric-client-clib-test/inc/symclient_test.h b/symmetric-client-clib-test/inc/symclient_test.h index a5cdc8f1d5..5d59a3f768 100644 --- a/symmetric-client-clib-test/inc/symclient_test.h +++ b/symmetric-client-clib-test/inc/symclient_test.h @@ -25,11 +25,13 @@ #include #include +#include #include "libsymclient.h" int SymEngineTest_CUnit(); int SymPropertiesTest_CUnit(); int SymStringBuilderTest_CUnit(); +int SymMapTest_CUnit(); #endif diff --git a/symmetric-client-clib-test/run/symclient_test.launch b/symmetric-client-clib-test/run/symclient_test.launch index 58b575e51a..7d4ad7e92a 100644 --- a/symmetric-client-clib-test/run/symclient_test.launch +++ b/symmetric-client-clib-test/run/symclient_test.launch @@ -20,7 +20,7 @@ - + diff --git a/symmetric-client-clib-test/src/symclient_test.c b/symmetric-client-clib-test/src/symclient_test.c index f61e34289f..16dda2731a 100644 --- a/symmetric-client-clib-test/src/symclient_test.c +++ b/symmetric-client-clib-test/src/symclient_test.c @@ -25,7 +25,10 @@ int main() { return CU_get_error(); } - if (SymEngineTest_CUnit() != CUE_SUCCESS || + if ( + SymEngineTest_CUnit() != CUE_SUCCESS || + //SymMapTest_CUnit() != CUE_SUCCESS || + //SymListTest_CUnit() != CUE_SUCCESS || //SymStringBuilderTest_CUnit() != CUE_SUCCESS || //SymPropertiesTest_CUnit() != CUE_SUCCESS) { 1==0) { diff --git a/symmetric-client-clib-test/src/util/ListTest.c b/symmetric-client-clib-test/src/util/ListTest.c new file mode 100644 index 0000000000..f10789cadd --- /dev/null +++ b/symmetric-client-clib-test/src/util/ListTest.c @@ -0,0 +1,121 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "symclient_test.h" + +typedef struct { + int i; +} myobj; + +/* +void SymListTest_test1() { + SymList *list = SymList_new(NULL); + list->add(list, "hola"); + list->add(list, ""); + list->add(list, NULL); + list->add(list, "bonjour"); + + CU_ASSERT(strcmp(list->get(list, 0), "hola") == 0); + CU_ASSERT(strcmp(list->get(list, 1), "") == 0); + CU_ASSERT(list->get(list, 2) == NULL); + CU_ASSERT(strcmp(list->get(list, 3), "bonjour") == 0); + CU_ASSERT(list->size == 4); + + char **array = (char **) list->to_array(list); + CU_ASSERT(strcmp(array[0], "hola") == 0); + CU_ASSERT(array[1] != NULL); + CU_ASSERT(strcmp(array[1], "") == 0); + CU_ASSERT(array[2] == NULL); + CU_ASSERT(strcmp(array[3], "bonjour") == 0); + CU_ASSERT(array[4] == NULL); + + int i; + for (i = 0; array[i] != NULL; i++) { + printf("%d is %s\n", i, array[i]); + } + + + list->destroy(list); +} + +void SymListTest_test2() { + myobj myobjs[3]; + myobjs[0].i = 0; + myobjs[1].i = 1; + myobjs[2].i = 2; + + SymList *list = SymList_new(NULL); + list->add(list, &myobjs[0]); + list->add(list, NULL); + list->add(list, &myobjs[2]); + CU_ASSERT(((myobj *) list->get(list, 0))->i == 0); + CU_ASSERT(((myobj *) list->get(list, 1)) == NULL); + CU_ASSERT(((myobj *) list->get(list, 2))->i == 2); + + myobj **array = (myobj **) list->to_array(list); + CU_ASSERT(array[0]->i == 0); + CU_ASSERT(array[1] == NULL); + CU_ASSERT(array[2]->i == 2); + + int i; + for (i = 0; array[i] != NULL; i++) { + printf("%d is %d\n", i, array[i]->i); + } + + + list->destroy(list); +} + +void SymListTest_test3() { + SymList *list = SymList_new(NULL); + list->add(list, "one string"); + list->add(list, "two thing"); + list->add(list, "red ping"); + list->add(list, "blue ring"); + + char **array = (char **) list->to_array_slice(list, 1, 2); + CU_ASSERT(strcmp(array[0], "two thing") == 0); + + int i; + for (i = 0; array[i] != NULL; i++) { + printf("%d is %s\n", i, array[i]); + } + + + list->destroy(list); +} + +*/ + +int SymListTest_CUnit() { + CU_pSuite suite = CU_add_suite("SymListTest", NULL, NULL); + if (suite == NULL) { + return CUE_NOSUITE; + } + + /* + if (CU_add_test(suite, "SymListTest_test1", SymListTest_test1) == NULL || + CU_add_test(suite, "SymListTest_test2", SymListTest_test2) == NULL || + CU_add_test(suite, "SymListTest_test3", SymListTest_test3) == NULL) { + return CUE_NOTEST; + } + */ + return CUE_SUCCESS; +} diff --git a/symmetric-client-clib-test/src/util/MapTest.c b/symmetric-client-clib-test/src/util/MapTest.c new file mode 100644 index 0000000000..4a250f5e35 --- /dev/null +++ b/symmetric-client-clib-test/src/util/MapTest.c @@ -0,0 +1,104 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "symclient_test.h" + +static void SymMapTest_test_stringmap(int keySize, char **keys, char **values, int mapSize) { + SymMap *map = SymMap_new(NULL, mapSize); + int i; + for (i = 0; i < keySize; i++) { + map->put(map, keys[i], values[i], strlen(values[i])); + } + + for (i = 0; i < keySize; i++) { + char *out = (char *) map->get(map, keys[i]); + CU_ASSERT(out != NULL); + if (out != NULL) { + CU_ASSERT(strcmp(out, values[i]) == 0); + } + } + map->destroy(map); +} + +void SymMapTest_test1() { + char *keys[] = { "Hello", "Hi", "Welcome", "Greetings" }; + char *values[] = { "Goodbye", "Hey", "Thank you", "Later" }; + + SymMapTest_test_stringmap(4, keys, values, 1); + SymMapTest_test_stringmap(4, keys, values, 4); + SymMapTest_test_stringmap(4, keys, values, 8); +} + +void SymMapTest_test2() { + char *keys[] = { "1", "2", "Three" }; + char *values[] = { "One", "", "3" }; + + SymMapTest_test_stringmap(3, keys, values, 0); + SymMapTest_test_stringmap(3, keys, values, 1); + SymMapTest_test_stringmap(3, keys, values, 2); + SymMapTest_test_stringmap(3, keys, values, 3); +} + +void SymMapTest_test3() { + char *columnNames[] = { "id", "name", "description", "create_time" }; + SymTable *table = SymTable_new_with_fullname(NULL, "mydb", "dbo", "table1"); + table->columns = SymList_new(NULL); + int i; + for (i = 0; i < 4; i++) { + table->columns->add(table->columns, SymColumn_new(NULL, columnNames[i], 0)); + } + + SymMap *map = SymMap_new(NULL, 100); + map->put(map, table->name, table, sizeof(SymTable)); + SymTable *out = map->get(map, table->name); + + CU_ASSERT(out != NULL); + if (out) { + CU_ASSERT(out->columns != NULL); + if (out->columns) { + SymIterator *iter = out->columns->iterator(out->columns); + while (iter->has_next(iter)) { + SymColumn *column = (SymColumn *) iter->next(iter); + CU_ASSERT(column->name != NULL); + if (column->name) { + CU_ASSERT(strcmp(column->name, columnNames[iter->index]) == 0); + } + } + iter->destroy(iter); + } + } + + table->destroy(table); + map->destroy(map); +} + +int SymMapTest_CUnit() { + CU_pSuite suite = CU_add_suite("SymMapTest", NULL, NULL); + if (suite == NULL) { + return CUE_NOSUITE; + } + + if (CU_add_test(suite, "SymMapTest_test1", SymMapTest_test1) == NULL || + CU_add_test(suite, "SymMapTest_test2", SymMapTest_test2) == NULL || + CU_add_test(suite, "SymMapTest_test3", SymMapTest_test3) == NULL) { + return CUE_NOTEST; + } + return CUE_SUCCESS; +} diff --git a/symmetric-client-clib-test/src/util/StringBuilderTest.c b/symmetric-client-clib-test/src/util/StringBuilderTest.c index 08bf4be0b4..503202353f 100644 --- a/symmetric-client-clib-test/src/util/StringBuilderTest.c +++ b/symmetric-client-clib-test/src/util/StringBuilderTest.c @@ -27,7 +27,7 @@ void SymStringBuilderTest_test1() { sb->append(sb, ",bonjour"); sb->append(sb, ",guten tag"); sb->append(sb, ",ciao"); - CU_ASSERT(strcmp(sb->to_string(sb), "hello,hola,salute,bonjour,guten tag,ciao") == 0); + CU_ASSERT(strcmp(sb->str, "hello,hola,salute,bonjour,guten tag,ciao") == 0); sb->destroy(sb); } @@ -35,7 +35,7 @@ void SymStringBuilderTest_test2() { SymStringBuilder *sb = SymStringBuilder_new_with_size(1); sb->append(sb, "como "); sb->append(sb, "t'allez-vous"); - CU_ASSERT(strcmp(sb->to_string(sb), "como t'allez-vous") == 0); + CU_ASSERT(strcmp(sb->str, "como t'allez-vous") == 0); sb->destroy(sb); } @@ -44,7 +44,7 @@ void SymStringBuilderTest_test3() { sb->append(sb, "1"); sb->append(sb, "2"); sb->append(sb, "3"); - CU_ASSERT(strcmp(sb->to_string(sb), "123") == 0); + CU_ASSERT(strcmp(sb->str, "123") == 0); sb->destroy(sb); } diff --git a/symmetric-client-clib/Debug/makefile b/symmetric-client-clib/Debug/makefile index f0f0a434ce..bc294aafd1 100644 --- a/symmetric-client-clib/Debug/makefile +++ b/symmetric-client-clib/Debug/makefile @@ -16,6 +16,10 @@ RM := rm -rf -include src/io/writer/subdir.mk -include src/io/reader/subdir.mk -include src/io/data/subdir.mk +-include src/db/sqlite/subdir.mk +-include src/db/sql/subdir.mk +-include src/db/platform/sqlite/subdir.mk +-include src/db/platform/subdir.mk -include src/db/model/subdir.mk -include src/db/subdir.mk -include src/core/subdir.mk diff --git a/symmetric-client-clib/Debug/sources.mk b/symmetric-client-clib/Debug/sources.mk index cded5b9b8e..2e630f0664 100644 --- a/symmetric-client-clib/Debug/sources.mk +++ b/symmetric-client-clib/Debug/sources.mk @@ -16,6 +16,10 @@ SUBDIRS := \ src/core \ src/db \ src/db/model \ +src/db/platform \ +src/db/platform/sqlite \ +src/db/sql \ +src/db/sqlite \ src/io/data \ src/io/reader \ src/io/writer \ diff --git a/symmetric-client-clib/Debug/src/db/model/subdir.mk b/symmetric-client-clib/Debug/src/db/model/subdir.mk index 08d069f880..4e439e2def 100644 --- a/symmetric-client-clib/Debug/src/db/model/subdir.mk +++ b/symmetric-client-clib/Debug/src/db/model/subdir.mk @@ -4,12 +4,15 @@ # Add inputs and outputs from these tool invocations to the build variables C_SRCS += \ +../src/db/model/Column.c \ ../src/db/model/Table.c OBJS += \ +./src/db/model/Column.o \ ./src/db/model/Table.o C_DEPS += \ +./src/db/model/Column.d \ ./src/db/model/Table.d diff --git a/symmetric-client-clib/Debug/src/db/platform/sqlite/subdir.mk b/symmetric-client-clib/Debug/src/db/platform/sqlite/subdir.mk new file mode 100644 index 0000000000..39f08bcf2e --- /dev/null +++ b/symmetric-client-clib/Debug/src/db/platform/sqlite/subdir.mk @@ -0,0 +1,33 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +../src/db/platform/sqlite/SqliteDdlReader.c \ +../src/db/platform/sqlite/SqlitePlatform.c \ +../src/db/platform/sqlite/SqliteSqlTemplate.c \ +../src/db/platform/sqlite/SqliteSqlTransaction.c + +OBJS += \ +./src/db/platform/sqlite/SqliteDdlReader.o \ +./src/db/platform/sqlite/SqlitePlatform.o \ +./src/db/platform/sqlite/SqliteSqlTemplate.o \ +./src/db/platform/sqlite/SqliteSqlTransaction.o + +C_DEPS += \ +./src/db/platform/sqlite/SqliteDdlReader.d \ +./src/db/platform/sqlite/SqlitePlatform.d \ +./src/db/platform/sqlite/SqliteSqlTemplate.d \ +./src/db/platform/sqlite/SqliteSqlTransaction.d + + +# Each subdirectory must supply rules for building sources it contributes +src/db/platform/sqlite/%.o: ../src/db/platform/sqlite/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + gcc -I"/home/elong/git/3.7/symmetric-ds/symmetric-client-clib/inc" -O0 -g3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/symmetric-client-clib/Debug/src/db/platform/subdir.mk b/symmetric-client-clib/Debug/src/db/platform/subdir.mk new file mode 100644 index 0000000000..9f70c4cdee --- /dev/null +++ b/symmetric-client-clib/Debug/src/db/platform/subdir.mk @@ -0,0 +1,30 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +../src/db/platform/DatabasePlatform.c \ +../src/db/platform/DatabasePlatformFactory.c \ +../src/db/platform/DdlReader.c + +OBJS += \ +./src/db/platform/DatabasePlatform.o \ +./src/db/platform/DatabasePlatformFactory.o \ +./src/db/platform/DdlReader.o + +C_DEPS += \ +./src/db/platform/DatabasePlatform.d \ +./src/db/platform/DatabasePlatformFactory.d \ +./src/db/platform/DdlReader.d + + +# Each subdirectory must supply rules for building sources it contributes +src/db/platform/%.o: ../src/db/platform/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + gcc -I"/home/elong/git/3.7/symmetric-ds/symmetric-client-clib/inc" -O0 -g3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/symmetric-client-clib/Debug/src/db/sql/subdir.mk b/symmetric-client-clib/Debug/src/db/sql/subdir.mk new file mode 100644 index 0000000000..40f430e9f8 --- /dev/null +++ b/symmetric-client-clib/Debug/src/db/sql/subdir.mk @@ -0,0 +1,27 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +../src/db/sql/DmlStatement.c \ +../src/db/sql/Row.c + +OBJS += \ +./src/db/sql/DmlStatement.o \ +./src/db/sql/Row.o + +C_DEPS += \ +./src/db/sql/DmlStatement.d \ +./src/db/sql/Row.d + + +# Each subdirectory must supply rules for building sources it contributes +src/db/sql/%.o: ../src/db/sql/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + gcc -I"/home/elong/git/3.7/symmetric-ds/symmetric-client-clib/inc" -O0 -g3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/symmetric-client-clib/Debug/src/db/sqlite/subdir.mk b/symmetric-client-clib/Debug/src/db/sqlite/subdir.mk new file mode 100644 index 0000000000..90eb92e76d --- /dev/null +++ b/symmetric-client-clib/Debug/src/db/sqlite/subdir.mk @@ -0,0 +1,24 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +../src/db/sqlite/SqliteDialect.c + +OBJS += \ +./src/db/sqlite/SqliteDialect.o + +C_DEPS += \ +./src/db/sqlite/SqliteDialect.d + + +# Each subdirectory must supply rules for building sources it contributes +src/db/sqlite/%.o: ../src/db/sqlite/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + gcc -I"/home/elong/git/3.7/symmetric-ds/symmetric-client-clib/inc" -O0 -g3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/symmetric-client-clib/Debug/src/db/subdir.mk b/symmetric-client-clib/Debug/src/db/subdir.mk index 96c49fe0b0..9b68ead087 100644 --- a/symmetric-client-clib/Debug/src/db/subdir.mk +++ b/symmetric-client-clib/Debug/src/db/subdir.mk @@ -4,26 +4,14 @@ # Add inputs and outputs from these tool invocations to the build variables C_SRCS += \ -../src/db/DatabasePlatform.c \ -../src/db/DatabasePlatformFactory.c \ -../src/db/SqliteDialect.c \ -../src/db/SqlitePlatform.c \ ../src/db/SymDialect.c \ ../src/db/SymDialectFactory.c OBJS += \ -./src/db/DatabasePlatform.o \ -./src/db/DatabasePlatformFactory.o \ -./src/db/SqliteDialect.o \ -./src/db/SqlitePlatform.o \ ./src/db/SymDialect.o \ ./src/db/SymDialectFactory.o C_DEPS += \ -./src/db/DatabasePlatform.d \ -./src/db/DatabasePlatformFactory.d \ -./src/db/SqliteDialect.d \ -./src/db/SqlitePlatform.d \ ./src/db/SymDialect.d \ ./src/db/SymDialectFactory.d diff --git a/symmetric-client-clib/Debug/src/io/data/subdir.mk b/symmetric-client-clib/Debug/src/io/data/subdir.mk index 0398dbc8a9..d00000a430 100644 --- a/symmetric-client-clib/Debug/src/io/data/subdir.mk +++ b/symmetric-client-clib/Debug/src/io/data/subdir.mk @@ -5,15 +5,18 @@ # Add inputs and outputs from these tool invocations to the build variables C_SRCS += \ ../src/io/data/Batch.c \ -../src/io/data/CsvData.c +../src/io/data/CsvData.c \ +../src/io/data/DataContext.c OBJS += \ ./src/io/data/Batch.o \ -./src/io/data/CsvData.o +./src/io/data/CsvData.o \ +./src/io/data/DataContext.o C_DEPS += \ ./src/io/data/Batch.d \ -./src/io/data/CsvData.d +./src/io/data/CsvData.d \ +./src/io/data/DataContext.d # Each subdirectory must supply rules for building sources it contributes diff --git a/symmetric-client-clib/Debug/src/util/subdir.mk b/symmetric-client-clib/Debug/src/util/subdir.mk index 2eda7e548a..26876d95ac 100644 --- a/symmetric-client-clib/Debug/src/util/subdir.mk +++ b/symmetric-client-clib/Debug/src/util/subdir.mk @@ -4,18 +4,24 @@ # Add inputs and outputs from these tool invocations to the build variables C_SRCS += \ -../src/util/ArrayBuilder.c \ +../src/util/List.c \ +../src/util/Map.c \ ../src/util/Properties.c \ +../src/util/StringArray.c \ ../src/util/StringBuilder.c OBJS += \ -./src/util/ArrayBuilder.o \ +./src/util/List.o \ +./src/util/Map.o \ ./src/util/Properties.o \ +./src/util/StringArray.o \ ./src/util/StringBuilder.o C_DEPS += \ -./src/util/ArrayBuilder.d \ +./src/util/List.d \ +./src/util/Map.d \ ./src/util/Properties.d \ +./src/util/StringArray.d \ ./src/util/StringBuilder.d diff --git a/symmetric-client-clib/inc/core/SymEngine.h b/symmetric-client-clib/inc/core/SymEngine.h index 9b332a66d9..e202872159 100644 --- a/symmetric-client-clib/inc/core/SymEngine.h +++ b/symmetric-client-clib/inc/core/SymEngine.h @@ -25,10 +25,10 @@ #include #include #include -#include "db/DatabasePlatformFactory.h" +#include "db/platform/DatabasePlatformFactory.h" #include "db/SymDialectFactory.h" #include "db/SymDialect.h" -#include "db/SqliteDialect.h" +#include "db/sqlite/SqliteDialect.h" #include "service/TriggerRouterService.h" #include "service/ParameterService.h" #include "service/PullService.h" diff --git a/symmetric-client-clib/inc/db/SymDialect.h b/symmetric-client-clib/inc/db/SymDialect.h index f5dd44f6b3..5e6c12d288 100644 --- a/symmetric-client-clib/inc/db/SymDialect.h +++ b/symmetric-client-clib/inc/db/SymDialect.h @@ -24,7 +24,7 @@ #include #include #include -#include "db/DatabasePlatform.h" +#include "db/platform/DatabasePlatform.h" typedef struct { SymDatabasePlatform *platform; diff --git a/symmetric-client-clib/inc/db/SymDialectFactory.h b/symmetric-client-clib/inc/db/SymDialectFactory.h index 15716300e4..45eda24511 100644 --- a/symmetric-client-clib/inc/db/SymDialectFactory.h +++ b/symmetric-client-clib/inc/db/SymDialectFactory.h @@ -25,7 +25,7 @@ #include #include #include "db/SymDialect.h" -#include "db/SqliteDialect.h" +#include "db/sqlite/SqliteDialect.h" SymDialect * SymDialectFactory_create(SymDatabasePlatform *platform); diff --git a/symmetric-client-clib/inc/db/model/Column.h b/symmetric-client-clib/inc/db/model/Column.h new file mode 100644 index 0000000000..f0ca363d22 --- /dev/null +++ b/symmetric-client-clib/inc/db/model/Column.h @@ -0,0 +1,40 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_COLUMN_H +#define SYM_COLUMN_H + +#include +#include +#include "util/StringBuilder.h" + +typedef struct SymColumn { + char *name; + unsigned short isPrimaryKey; + unsigned short isRequired; + int sqlType; + char * (*to_string)(struct SymColumn *this); + void (*destroy)(void *this); +} SymColumn; + +SymColumn * SymColumn_new(SymColumn *this, char *name, unsigned short isPrimaryKey); +void SymColumn_destroy(SymColumn *this); + +#endif diff --git a/symmetric-client-clib/inc/db/model/Table.h b/symmetric-client-clib/inc/db/model/Table.h index c715deb548..ec7b4c5302 100644 --- a/symmetric-client-clib/inc/db/model/Table.h +++ b/symmetric-client-clib/inc/db/model/Table.h @@ -23,20 +23,29 @@ #include #include +#include +#include "util/List.h" +#include "util/StringBuilder.h" +#include "db/model/Column.h" -typedef struct { +typedef struct SymTable { char *catalog; char *schema; char *name; - char **keys; - char **columns; - int sizeKeys; - int sizeColumns; - void (*set)(char **strField, char *str); - void (*set_array)(char ***arrayField, int *sizeField, char **array, int sizeArray); - void (*destroy)(void *this); + SymList *columns; + struct SymTable * (*copy_and_filter_columns)(struct SymTable *this, struct SymTable *source, unsigned short setPrimaryKeys); + void (*copy_column_types_from)(struct SymTable *this, struct SymTable *source); + SymColumn * (*find_column)(struct SymTable *this, char *name, unsigned short caseSensitive); + char * (*to_string)(struct SymTable *this); + void (*destroy)(struct SymTable *this); } SymTable; SymTable * SymTable_new(SymTable *this); +SymTable * SymTable_new_with_name(SymTable *this, char *name); + +SymTable * SymTable_new_with_fullname(SymTable *this, char *catalog, char *schema, char *name); + +char * SymTable_get_full_table_name(SymTable *this, char *delimiterToken, char *catalogSeparator, char *schemaSeparator); + #endif diff --git a/symmetric-client-clib/inc/db/platform/DatabaseInfo.h b/symmetric-client-clib/inc/db/platform/DatabaseInfo.h new file mode 100644 index 0000000000..2c47d108df --- /dev/null +++ b/symmetric-client-clib/inc/db/platform/DatabaseInfo.h @@ -0,0 +1,33 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_DATABASE_INFO_H +#define SYM_DATABASE_INFO_H + +#include +#include + +typedef struct { + char *delimiterToken; + char *catalogSeparator; + char *schemaSeparator; +} SymDatabaseInfo; + +#endif diff --git a/symmetric-client-clib/inc/db/DatabasePlatform.h b/symmetric-client-clib/inc/db/platform/DatabasePlatform.h similarity index 65% rename from symmetric-client-clib/inc/db/DatabasePlatform.h rename to symmetric-client-clib/inc/db/platform/DatabasePlatform.h index 67470b79bf..e1e7ed765a 100644 --- a/symmetric-client-clib/inc/db/DatabasePlatform.h +++ b/symmetric-client-clib/inc/db/platform/DatabasePlatform.h @@ -25,6 +25,10 @@ #include #include #include "service/ParameterService.h" +#include "db/model/Table.h" +#include "db/platform/DdlReader.h" +#include "db/sql/SqlTemplate.h" +#include "db/platform/DatabaseInfo.h" #define SYM_DATABASE_SQLITE "sqlite" #define SYM_DATABASE_UNDEFINED "undefined" @@ -33,18 +37,20 @@ typedef struct { SymProperties *properties; char *name; char *version; - int (*execute_sql)( - void *this, /* SymDatabasePlatform object */ - const char *sql, /* SQL to execute */ - int (*callback)(void*, int, char **, char **), /* Callback function */ - void *arg, /* 1st argument to callback */ - char **errorMessage /* Error message written here */ - ); + char *defaultCatalog; + char *defaultSchema; + SymDatabaseInfo databaseInfo; + SymDdlReader *ddlReader; + int (*execute_sql)(void *this, const char *sql, + int (*callback)(void *userData, int sizeColumns, char **columnNames, char **columnValues), + void *userData, char **errorMessage); int (*table_exists)(void *this, char *tableName); - void (*free)(void *data); + SymSqlTemplate * (*get_sql_template)(void *this); + SymTable * (*get_table_from_cache)(void *this, char *catalog, char *schema, char *tableName, unsigned int forceReread); + SymTable * (*read_table_from_database)(void *this, char *catalog, char *schema, char *tableName); void (*destroy)(void *this); } SymDatabasePlatform; -SymDatabasePlatform * SymDatabasePlatform_new(SymDatabasePlatform *this, SymProperties *properties); +SymDatabasePlatform * SymDatabasePlatform_new(SymDatabasePlatform *this); #endif diff --git a/symmetric-client-clib/inc/db/DatabasePlatformFactory.h b/symmetric-client-clib/inc/db/platform/DatabasePlatformFactory.h similarity index 92% rename from symmetric-client-clib/inc/db/DatabasePlatformFactory.h rename to symmetric-client-clib/inc/db/platform/DatabasePlatformFactory.h index a9a191cbfe..5314a68190 100644 --- a/symmetric-client-clib/inc/db/DatabasePlatformFactory.h +++ b/symmetric-client-clib/inc/db/platform/DatabasePlatformFactory.h @@ -24,8 +24,8 @@ #include #include #include -#include "db/DatabasePlatform.h" -#include "db/SqlitePlatform.h" +#include "db/platform/DatabasePlatform.h" +#include "db/platform/sqlite/SqlitePlatform.h" SymDatabasePlatform * SymDatabasePlatformFactory_create(SymProperties *properties); diff --git a/symmetric-client-clib/inc/db/platform/DdlReader.h b/symmetric-client-clib/inc/db/platform/DdlReader.h new file mode 100644 index 0000000000..654226765f --- /dev/null +++ b/symmetric-client-clib/inc/db/platform/DdlReader.h @@ -0,0 +1,35 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_DDL_READER_H +#define SYM_DDL_READER_H + +#include +#include +#include "db/model/Table.h" + +typedef struct { + SymTable * (*read_table)(void *this, char *catalog, char *schema, char *tableName); + void (*destroy)(void *this); +} SymDdlReader; + +SymDdlReader * SymDdlReader_new(SymDdlReader *this); + +#endif diff --git a/symmetric-client-clib/inc/db/platform/sqlite/SqliteDdlReader.h b/symmetric-client-clib/inc/db/platform/sqlite/SqliteDdlReader.h new file mode 100644 index 0000000000..d5e4d72893 --- /dev/null +++ b/symmetric-client-clib/inc/db/platform/sqlite/SqliteDdlReader.h @@ -0,0 +1,38 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_SQLITE_DDL_READER_H +#define SYM_SQLITE_DDL_READER_H + +#include +#include +#include "db/platform/DatabasePlatform.h" +#include "db/model/Table.h" +#include "util/List.h" + +typedef struct { + SymDdlReader super; + SymDatabasePlatform *platform; + void (*destroy)(void *this); +} SymSqliteDdlReader; + +SymSqliteDdlReader * SymSqliteDdlReader_new(SymSqliteDdlReader *this, SymDatabasePlatform *platform); + +#endif diff --git a/symmetric-client-clib/inc/db/SqlitePlatform.h b/symmetric-client-clib/inc/db/platform/sqlite/SqlitePlatform.h similarity index 87% rename from symmetric-client-clib/inc/db/SqlitePlatform.h rename to symmetric-client-clib/inc/db/platform/sqlite/SqlitePlatform.h index 6f5dd6c0a7..1a6b4da897 100644 --- a/symmetric-client-clib/inc/db/SqlitePlatform.h +++ b/symmetric-client-clib/inc/db/platform/sqlite/SqlitePlatform.h @@ -24,11 +24,14 @@ #include #include #include -#include "db/DatabasePlatform.h" +#include "db/platform/DatabasePlatform.h" +#include "db/platform/sqlite/SqliteDdlReader.h" +#include "db/sqlite/SqliteSqlTemplate.h" typedef struct { SymDatabasePlatform super; sqlite3 *db; + SymSqlTemplate *sqlTemplate; } SymSqlitePlatform; SymSqlitePlatform * SymSqlitePlatform_new(SymSqlitePlatform *this, SymProperties *properties); diff --git a/symmetric-client-clib/inc/db/sql/DmlStatement.h b/symmetric-client-clib/inc/db/sql/DmlStatement.h new file mode 100644 index 0000000000..ada01b3ca0 --- /dev/null +++ b/symmetric-client-clib/inc/db/sql/DmlStatement.h @@ -0,0 +1,47 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_DML_STATEMENT_H +#define SYM_DML_STATEMENT_H + +#include +#include +#include "db/model/Table.h" +#include "db/platform/DatabaseInfo.h" +#include "util/List.h" + +typedef enum { + SYM_DML_TYPE_INSERT, SYM_DML_TYPE_UPDATE, SYM_DML_TYPE_DELETE, SYM_DML_TYPE_SELECT +} SymDmlType; + +typedef struct { + SymDmlType dmlType; + SymTable *table; + SymDatabaseInfo *databaseInfo; + SymList *nullKeyIndicators; + char *sql; + SymList *sqlTypes; + void (*destroy)(void *this); +} SymDmlStatement; + +SymDmlStatement * SymDmlStatement_new(SymDmlStatement *this, SymDmlType dmlType, SymTable *table, SymList *nullKeyIndicators, + SymDatabaseInfo *databaseInfo); + +#endif diff --git a/symmetric-client-clib/inc/db/sql/Row.h b/symmetric-client-clib/inc/db/sql/Row.h new file mode 100644 index 0000000000..86958a318c --- /dev/null +++ b/symmetric-client-clib/inc/db/sql/Row.h @@ -0,0 +1,46 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_ROW_H +#define SYM_ROW_H + +#include +#include +#include "util/Map.h" + +typedef struct { + char *value; + int sqlType; + int size; +} SymRowEntry; + +typedef struct { + SymMap *map; + void (*put)(void *this, char *columnName, char *value, int sqlType, int size); + char * (*get_string)(void *this, char *columnName); + int (*get_int)(void *this, char *columnName); + int (*get_size)(void *this, char *columnName); + int (*get_sql_type)(void *this, char *columnName); + void (*destroy)(void *this); +} SymRow; + +SymRow * SymRow_new(SymRow *this, int columnCount); + +#endif diff --git a/symmetric-client-clib/inc/db/sql/SqlTemplate.h b/symmetric-client-clib/inc/db/sql/SqlTemplate.h new file mode 100644 index 0000000000..be1e9be72a --- /dev/null +++ b/symmetric-client-clib/inc/db/sql/SqlTemplate.h @@ -0,0 +1,62 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_SQL_TEMPLATE_H +#define SYM_SQL_TEMPLATE_H + +#include +#include +#include "db/sql/SqlTransaction.h" +#include "db/sql/Row.h" +#include "util/List.h" +#include "util/StringArray.h" + +typedef enum { + SYM_SQL_TYPE_BIT, SYM_SQL_TYPE_TINYINT, SYM_SQL_TYPE_SMALLINT, SYM_SQL_TYPE_INTEGER, SYM_SQL_TYPE_BIGINT, + SYM_SQL_TYPE_FLOAT, SYM_SQL_TYPE_REAL, SYM_SQL_TYPE_DOUBLE, SYM_SQL_TYPE_NUMERIC, SYM_SQL_TYPE_DECIMAL, + SYM_SQL_TYPE_CHAR, SYM_SQL_TYPE_VARCHAR, SYM_SQL_TYPE_LONGVARCHAR, + SYM_SQL_TYPE_DATE, SYM_SQL_TYPE_TIME, SYM_SQL_TYPE_TIMESTAMP, + SYM_SQL_TYPE_BINARY, SYM_SQL_TYPE_VARBINARY, SYM_SQL_TYPE_LONGVARBINARY, + SYM_SQL_TYPE_NULL, + SYM_SQL_TYPE_OTHER, + SYM_SQL_TYPE_JAVA_OBJECT, + SYM_SQL_TYPE_DISTINCT, + SYM_SQL_TYPE_STRUCT, SYM_SQL_TYPE_ARRAY, + SYM_SQL_TYPE_BLOB, SYM_SQL_TYPE_CLOB, + SYM_SQL_TYPE_REF, SYM_SQL_TYPE_DATALINK, + SYM_SQL_TYPE_BOOLEAN, + SYM_SQL_TYPE_ROWID, + SYM_SQL_TYPE_NCHAR, SYM_SQL_TYPE_NVARCHAR, SYM_SQL_TYPE_LONGNVARCHAR, SYM_SQL_TYPE_NCLOB, + SYM_SQL_TYPE_SQLXML +} SymSqlType; + +typedef struct { + int (*query_for_int)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error); + char * (*query_for_string)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error); + SymList * (*query_for_list)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error); + void * (*query)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error, void *map_row(SymRow *row)); + int (*update)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error); + SymSqlTransaction * (*start_sql_transaction)(void *this); + void (*destroy)(void *this); +} SymSqlTemplate; + +SymSqlTemplate * SymSqlTemplate_new(SymSqlTemplate *this); + +#endif diff --git a/symmetric-client-clib/inc/db/sql/SqlTransaction.h b/symmetric-client-clib/inc/db/sql/SqlTransaction.h new file mode 100644 index 0000000000..4d849b7269 --- /dev/null +++ b/symmetric-client-clib/inc/db/sql/SqlTransaction.h @@ -0,0 +1,44 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_SQL_TRANSACTION_H +#define SYM_SQL_TRANSACTION_H + +#include +#include +#include "util/List.h" +#include "util/StringArray.h" + +typedef struct { + int (*query_for_int)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error); + char * (*query_for_string)(void *this, char *sql, SymStringArray *argss, SymList *sqlTypes, int *error); + void (*query)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error, void *callback); + int (*update)(void *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error); + void (*prepare)(void *this, char *sql); + int (*add_row)(void *this, SymStringArray *args, SymList *sqlTypes); + void (*commit)(void *this); + void (*rollback)(void *this); + void (*close)(void *this); + void (*destroy)(void *this); +} SymSqlTransaction; + +SymSqlTransaction * SymSqlTransaction_new(SymSqlTransaction *this); + +#endif diff --git a/symmetric-client-clib/inc/db/SqliteDialect.h b/symmetric-client-clib/inc/db/sqlite/SqliteDialect.h similarity index 100% rename from symmetric-client-clib/inc/db/SqliteDialect.h rename to symmetric-client-clib/inc/db/sqlite/SqliteDialect.h diff --git a/symmetric-client-clib/inc/db/sqlite/SqliteSqlTemplate.h b/symmetric-client-clib/inc/db/sqlite/SqliteSqlTemplate.h new file mode 100644 index 0000000000..98bb017bdb --- /dev/null +++ b/symmetric-client-clib/inc/db/sqlite/SqliteSqlTemplate.h @@ -0,0 +1,41 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_SQLITE_SQL_TEMPLATE_H +#define SYM_SQLITE_SQL_TEMPLATE_H + +#include +#include +#include +#include "db/sql/SqlTemplate.h" +#include "util/List.h" +#include "util/StringArray.h" + +typedef struct { + SymSqlTemplate super; + sqlite3 *db; + void (*destroy)(void *this); +} SymSqliteSqlTemplate; + +SymSqliteSqlTemplate * SymSqliteSqlTemplate_new(SymSqliteSqlTemplate *this, sqlite3 *db); + +#include "db/sqlite/SqliteSqlTransaction.h" + +#endif diff --git a/symmetric-client-clib/inc/db/sqlite/SqliteSqlTransaction.h b/symmetric-client-clib/inc/db/sqlite/SqliteSqlTransaction.h new file mode 100644 index 0000000000..9e6a959fe2 --- /dev/null +++ b/symmetric-client-clib/inc/db/sqlite/SqliteSqlTransaction.h @@ -0,0 +1,43 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_SQLITE_SQL_TRANSACTION_H +#define SYM_SQLITE_SQL_TRANSACTION_H + +#include +#include +#include +#include "db/sql/SqlTransaction.h" +#include "db/sqlite/SqliteSqlTemplate.h" +#include "util/List.h" + +typedef struct { + SymSqlTransaction super; + SymSqlTemplate *sqlTemplate; + sqlite3 *db; + sqlite3_stmt *stmt; + char *sql; + unsigned short inTransaction; + void (*destroy)(void *this); +} SymSqliteSqlTransaction; + +SymSqliteSqlTransaction * SymSqliteSqlTransaction_new(SymSqliteSqlTransaction *this, SymSqliteSqlTemplate *sqlTemplate); + +#endif diff --git a/symmetric-client-clib/inc/io/data/Batch.h b/symmetric-client-clib/inc/io/data/Batch.h index 21c61225b0..260a065ea4 100644 --- a/symmetric-client-clib/inc/io/data/Batch.h +++ b/symmetric-client-clib/inc/io/data/Batch.h @@ -23,6 +23,7 @@ #include #include +#include "util/StringBuilder.h" typedef struct { long batchId; @@ -31,7 +32,6 @@ typedef struct { int initialLoad; char *channelId; unsigned short isIgnore; - void (*set)(char **strField, char *str); void (*destroy)(void *this); } SymBatch; diff --git a/symmetric-client-clib/inc/io/data/CsvData.h b/symmetric-client-clib/inc/io/data/CsvData.h index 4a5cdf1fc1..22a5526de0 100644 --- a/symmetric-client-clib/inc/io/data/CsvData.h +++ b/symmetric-client-clib/inc/io/data/CsvData.h @@ -23,6 +23,7 @@ #include #include +#include "util/StringArray.h" typedef enum { SYM_DATA_EVENT_INSERT, SYM_DATA_EVENT_UPDATE, SYM_DATA_EVENT_DELETE, SYM_DATA_EVENT_SQL @@ -32,23 +33,16 @@ typedef enum { SYM_CSV_ROW_DATA, SYM_CSV_OLD_DATA, SYM_CSV_PK_DATA } SymCsvType; -typedef struct { +typedef struct SymCsvData { SymDataEventType dataEventType; - char **rowData; - char **oldData; - char **pkData; - int sizeRowData; - int sizeOldData; - int sizePkData; + SymStringArray *rowData; + SymStringArray *oldData; + SymStringArray *pkData; - void (*set_array)(char ***arrayField, int *sizeField, char **array, int sizeArray); void (*reset)(void *this); void (*destroy)(void *this); } SymCsvData; SymCsvData * SymCsvData_new(SymCsvData *this); -SymCsvData * SymCsvData_new_with_settings(SymCsvData *this, SymDataEventType dataEventType, char **rowData, char **pkData, char **oldData, - int sizeRowData, int sizePkData, int sizeOldData); - #endif diff --git a/symmetric-client-clib/inc/io/reader/ProtocolDataReader.h b/symmetric-client-clib/inc/io/reader/ProtocolDataReader.h index 1e99401e35..35c4fd2a26 100644 --- a/symmetric-client-clib/inc/io/reader/ProtocolDataReader.h +++ b/symmetric-client-clib/inc/io/reader/ProtocolDataReader.h @@ -30,18 +30,25 @@ #include "io/writer/DataWriter.h" #include "model/Node.h" #include "transport/IncomingTransport.h" -#include "util/ArrayBuilder.h" +#include "util/StringArray.h" +#include "util/Map.h" #include "io/data/CsvConstants.h" +#include "io/data/CsvData.h" typedef struct { SymDataReader super; char *targetNodeId; SymDataWriter *writer; struct csv_parser *csvParser; - SymArrayBuilder *fields; + SymStringArray *fields; SymBatch *batch; SymTable *table; - SymCsvData *csvData; + char *catalog; + char *schema; + SymStringArray *oldData; + SymStringArray *keys; + SymMap *parsedTables; + unsigned short isError; } SymProtocolDataReader; SymProtocolDataReader * SymProtocolDataReader_new(SymProtocolDataReader *this, char *targetNodeId, SymDataWriter *writer); diff --git a/symmetric-client-clib/inc/io/writer/DataWriter.h b/symmetric-client-clib/inc/io/writer/DataWriter.h index b44fc1929f..017964649e 100644 --- a/symmetric-client-clib/inc/io/writer/DataWriter.h +++ b/symmetric-client-clib/inc/io/writer/DataWriter.h @@ -31,8 +31,8 @@ typedef struct { void (*open)(void *this); void (*close)(void *this); void (*start_batch)(void *this, SymBatch *batch); - int (*start_table)(void *this, SymTable *table); - void (*write)(void *this, SymCsvData *data); + unsigned short (*start_table)(void *this, SymTable *table); + unsigned short (*write)(void *this, SymCsvData *data); void (*end_table)(void *this, SymTable *table); void (*end_batch)(void *this, SymBatch *batch); void (*destroy)(void *this); diff --git a/symmetric-client-clib/inc/io/writer/DefaultDatabaseWriter.h b/symmetric-client-clib/inc/io/writer/DefaultDatabaseWriter.h index be5a6f0d9a..6447de4006 100644 --- a/symmetric-client-clib/inc/io/writer/DefaultDatabaseWriter.h +++ b/symmetric-client-clib/inc/io/writer/DefaultDatabaseWriter.h @@ -26,12 +26,24 @@ #include "io/writer/DataWriter.h" #include "io/data/Batch.h" #include "db/model/Table.h" +#include "db/platform/DatabasePlatform.h" #include "io/data/CsvData.h" +#include "util/StringArray.h" +#include "db/sql/DmlStatement.h" +#include "db/sql/SqlTemplate.h" +#include "db/sql/SqlTransaction.h" typedef struct { SymDataWriter super; + SymDatabasePlatform *platform; + SymSqlTransaction *sqlTransaction; + SymBatch *batch; + SymTable *sourceTable; + SymTable *targetTable; + SymDmlStatement *dmlStatement; + unsigned short isError; } SymDefaultDatabaseWriter; -SymDefaultDatabaseWriter * SymDefaultDatabaseWriter_new(SymDefaultDatabaseWriter *this); +SymDefaultDatabaseWriter * SymDefaultDatabaseWriter_new(SymDefaultDatabaseWriter *this, SymDatabasePlatform *platform); #endif diff --git a/symmetric-client-clib/inc/libsymclient.h b/symmetric-client-clib/inc/libsymclient.h index 0e31565732..061c69510e 100644 --- a/symmetric-client-clib/inc/libsymclient.h +++ b/symmetric-client-clib/inc/libsymclient.h @@ -20,7 +20,7 @@ */ #ifndef LIB_SYM_CLIENT_H #define LIB_SYM_CLIENT_H - + #include #include #include @@ -32,12 +32,15 @@ #include "model/NodeSecurity.h" #include "model/OutgoingBatch.h" #include "model/RemoteNodeStatus.h" +#include "db/model/Table.h" +#include "db/model/Column.h" #include "service/TriggerRouterService.h" #include "service/PullService.h" #include "service/PushService.h" #include "service/ParameterService.h" #include "util/StringBuilder.h" #include "util/Properties.h" +#include "util/Map.h" #include "common/Constants.h" typedef unsigned short _boolean; diff --git a/symmetric-client-clib/inc/model/Node.h b/symmetric-client-clib/inc/model/Node.h index 8957f4c2ce..c1ab43b87a 100644 --- a/symmetric-client-clib/inc/model/Node.h +++ b/symmetric-client-clib/inc/model/Node.h @@ -23,6 +23,8 @@ #include +#define SYM_VERSION "3.7.0" + typedef struct { char *nodeId; char *nodeGroupId; diff --git a/symmetric-client-clib/inc/service/DataLoaderService.h b/symmetric-client-clib/inc/service/DataLoaderService.h index efc6b8b924..cf8cacb34a 100644 --- a/symmetric-client-clib/inc/service/DataLoaderService.h +++ b/symmetric-client-clib/inc/service/DataLoaderService.h @@ -32,7 +32,7 @@ #include "model/RemoteNodeStatus.h" #include "io/reader/ProtocolDataReader.h" #include "io/writer/DefaultDatabaseWriter.h" -#include "db/DatabasePlatform.h" +#include "db/platform/DatabasePlatform.h" typedef struct { SymParameterService *parameterService; diff --git a/symmetric-client-clib/inc/util/List.h b/symmetric-client-clib/inc/util/List.h new file mode 100644 index 0000000000..45349cc383 --- /dev/null +++ b/symmetric-client-clib/inc/util/List.h @@ -0,0 +1,60 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef SYM_LIST_H +#define SYM_LIST_H + +#include +#include +#include +#include + +typedef struct { + void *object; + void *previous; + void *next; +} SymListItem; + +typedef struct { + int size; + int index; + SymListItem *currentItem; + unsigned short (*has_next)(void *this); + void * (*next)(void *this); + void (*destroy)(void *this); +} SymIterator; + +typedef struct { + SymListItem *head; + SymListItem *tail; + int size; + void (*add)(void *this, void *object); + void * (*get)(void *this, int index); + SymIterator * (*iterator)(void *this); + SymIterator * (*iterator_from_index)(void *this, int startIndex); + void (*reset)(void *this); + void (*reset_all)(void *this, void *destroy_object(void * object)); + void (*destroy)(void *this); + void (*destroy_all)(void *this, void *destroy_object(void * object)); +} SymList; + +SymList * SymList_new(SymList *this); + +#endif diff --git a/symmetric-client-clib/inc/util/Map.h b/symmetric-client-clib/inc/util/Map.h new file mode 100644 index 0000000000..435b7c89c6 --- /dev/null +++ b/symmetric-client-clib/inc/util/Map.h @@ -0,0 +1,28 @@ +#ifndef SYM_MAP_H +#define SYM_MAP_H + +#include +#include +#include +#include + +typedef struct { + char *key; + void *value; + int sizeBytes; + void *next; +} SymMapEntry; + + +typedef struct { + int size; + SymMapEntry **table; + void (*put)(void *this, char *key, void *value, int size); + void * (*get)(void *this, char *key); + int (*get_bytes_size)(void *this, char *key); + void (*destroy)(void *this); +} SymMap; + +SymMap *SymMap_new(SymMap *this, int size); + +#endif diff --git a/symmetric-client-clib/inc/util/Properties.h b/symmetric-client-clib/inc/util/Properties.h index 93daafafec..dc98487ef5 100644 --- a/symmetric-client-clib/inc/util/Properties.h +++ b/symmetric-client-clib/inc/util/Properties.h @@ -30,7 +30,7 @@ typedef struct { } SymProperty; typedef struct { - SymProperty **list; + SymProperty *propArray; int index; char * (*get)(void *this, char *key, char *defaultValue); void (*put)(void *this, char *key, char *value); diff --git a/symmetric-client-clib/inc/util/ArrayBuilder.h b/symmetric-client-clib/inc/util/StringArray.h similarity index 59% rename from symmetric-client-clib/inc/util/ArrayBuilder.h rename to symmetric-client-clib/inc/util/StringArray.h index 5b771d2fae..b7fa3e990a 100644 --- a/symmetric-client-clib/inc/util/ArrayBuilder.h +++ b/symmetric-client-clib/inc/util/StringArray.h @@ -18,42 +18,34 @@ * specific language governing permissions and limitations * under the License. */ -#ifndef SYM_ARRAY_BUILDER_H -#define SYM_ARRAY_BUILDER_H +#ifndef SYM_STRING_ARRAY_H +#define SYM_STRING_ARRAY_H +#include #include #include #include -typedef struct { - char *str; - void *previous; - void *next; -} SymArrayItem; +#define SYM_STRING_ARRAY_SIZE_INITIAL 100 +#define SYM_STRING_ARRAY_SIZE_INCREMENT 100 -typedef struct { - SymArrayItem *head; - SymArrayItem *tail; +typedef struct SymStringArray { + char **array; int size; + int sizeAllocated; + int sizeIncrement; void (*add)(void *this, char *src); - void (*addn)(void *this, const char *src, int length); + void (*addn)(void *this, char *src, int size); char * (*get)(void *this, int index); - char * (*to_string)(void *this, int index); - char ** (*to_array)(void *this); - char ** (*to_array_range)(void *this, int startIndex, int endIndex); + unsigned short (*contains)(void *this, char *findStr); + struct SymStringArray * (*subarray)(void *this, int startIndex, int endIndex); + void (*print)(void *this); void (*reset)(void *this); void (*destroy)(void *this); - void (*destroy_array)(char **array, int size); -} SymArrayBuilder; +} SymStringArray; -SymArrayBuilder * SymArrayBuilder_new(); +SymStringArray * SymStringArray_new(SymStringArray *this); -SymArrayBuilder * SymArrayBuilder_new_with_size(int size); - -SymArrayBuilder * SymArrayBuilder_new_with_string(char *str); - -void SymArrayBuilder_destroy_array(char **array, int size); - -char ** SymArrayBuilder_copy_array(char **array, int size); +SymStringArray * SymStringArray_new_with_size(SymStringArray *this, int sizeInitial, int sizeIncrement); #endif diff --git a/symmetric-client-clib/inc/util/StringBuilder.h b/symmetric-client-clib/inc/util/StringBuilder.h index 7a326fe432..fb36c440f1 100644 --- a/symmetric-client-clib/inc/util/StringBuilder.h +++ b/symmetric-client-clib/inc/util/StringBuilder.h @@ -21,19 +21,20 @@ #ifndef SYM_STRING_BUILDER_H #define SYM_STRING_BUILDER_H +#include #include #include #include #define SYM_STRING_BUILDER_SIZE 255 -typedef struct { +typedef struct SymStringBuilder { char *str; int pos; int size; - void (*append)(void *this, const char *src); - void (*appendn)(void *this, const char *src, int length); - void (*appendf)(void *this, const char *fmt, ...); + struct SymStringBuilder * (*append)(void *this, const char *src); + struct SymStringBuilder * (*appendn)(void *this, const char *src, int length); + struct SymStringBuilder * (*appendf)(void *this, const char *fmt, ...); char * (*to_string)(void * this); void (*reset)(void * this); void (*destroy)(void * this); @@ -48,4 +49,6 @@ SymStringBuilder * SymStringBuilder_new_with_string(char *str); char * SymStringBuilder_copy(char *str); +void SymStringBuilder_copy_to_field(char **strField, char *str); + #endif diff --git a/symmetric-client-clib/src/db/DatabasePlatform.c b/symmetric-client-clib/src/db/model/Column.c similarity index 53% rename from symmetric-client-clib/src/db/DatabasePlatform.c rename to symmetric-client-clib/src/db/model/Column.c index 1fe1ee613d..9922eb3183 100644 --- a/symmetric-client-clib/src/db/DatabasePlatform.c +++ b/symmetric-client-clib/src/db/model/Column.c @@ -18,32 +18,30 @@ * specific language governing permissions and limitations * under the License. */ -#include "db/DatabasePlatform.h" +#include "db/model/Column.h" -int SymDatabasePlatform_table_exists(SymDatabasePlatform *this) { - return 0; +char * SymColumn_to_string(SymColumn *this) { + SymStringBuilder *sb = SymStringBuilder_new_with_string("Column [name="); + sb->append(sb, this->name); + sb->append(sb, "; type=")->appendf(sb, "%d", this->sqlType); + sb->append(sb, "; required=")->appendf(sb, "%d", this->isRequired); + sb->append(sb, "; pk=")->appendf(sb, "%d", this->isPrimaryKey); + sb->append(sb, "]"); + return sb->destroy_and_return(sb); } -int SymDatabasePlatform_execute_sql(SymDatabasePlatform *this) { - return 0; -} - -void SymDatabasePlatform_free(SymDatabasePlatform *this) { -} - -void SymDatabasePlatform_destroy(SymDatabasePlatform *this) { +void SymColumn_destroy(SymColumn *this) { + free(this->name); free(this); } -SymDatabasePlatform * SymDatabasePlatform_new(SymDatabasePlatform *this, SymProperties *properties) { +SymColumn * SymColumn_new(SymColumn *this, char *name, unsigned short isPrimaryKey) { if (this == NULL) { - this = (SymDatabasePlatform *) calloc(1, sizeof(SymDatabasePlatform)); + this = (SymColumn *) calloc(1, sizeof(SymColumn)); } - this->name = SYM_DATABASE_UNDEFINED; - this->properties = properties; - this->execute_sql = (void *) &SymDatabasePlatform_execute_sql; - this->table_exists = (void *) &SymDatabasePlatform_table_exists; - this->free = (void *) &SymDatabasePlatform_free; - this->destroy = (void *) &SymDatabasePlatform_destroy; + this->name = SymStringBuilder_copy(name); + this->isPrimaryKey = isPrimaryKey; + this->to_string = (void *) &SymColumn_to_string; + this->destroy = (void *) &SymColumn_destroy; return this; } diff --git a/symmetric-client-clib/src/db/model/Table.c b/symmetric-client-clib/src/db/model/Table.c index 3aab32af1a..914c7e9d54 100644 --- a/symmetric-client-clib/src/db/model/Table.c +++ b/symmetric-client-clib/src/db/model/Table.c @@ -20,23 +20,92 @@ */ #include "db/model/Table.h" -void SymTable_set(char **strField, char *str) { - free(*strField); - *strField = SymStringBuilder_copy(str); +char * SymTable_get_full_table_name(SymTable *this, char *delimiterToken, char *catalogSeparator, char *schemaSeparator) { + SymStringBuilder *sb = SymStringBuilder_new(); + if (this->catalog != NULL) { + sb->append(sb, delimiterToken)->append(sb, this->catalog)->append(sb, delimiterToken)->append(sb, catalogSeparator); + } + if (this->schema != NULL) { + sb->append(sb, delimiterToken)->append(sb, this->schema)->append(sb, delimiterToken)->append(sb, schemaSeparator); + } + sb->append(sb, delimiterToken)->append(sb, this->name)->append(sb, delimiterToken); + return sb->destroy_and_return(sb); +} + +SymColumn * SymTable_find_column(SymTable *this, char *name, unsigned short caseSensitive) { + SymColumn *column = NULL; + SymIterator *iter = this->columns->iterator(this->columns); + while (iter->has_next(iter)) { + SymColumn *nextColumn = (SymColumn *) iter->next(iter); + if ((caseSensitive && strcmp(name, nextColumn->name) == 0) || strcasecmp(name, nextColumn->name) == 0) { + column = nextColumn; + break; + } + } + iter->destroy(iter); + return column; } -void SymTable_set_array(char ***arrayField, int *sizeField, char **array, int sizeArray) { - SymArrayBuilder_destroy_array(*arrayField, *sizeField); - *arrayField = SymArrayBuilder_copy_array(array, sizeArray); - *sizeField = sizeArray; +SymTable * SymTable_copy_and_filter_columns(SymTable *this, SymTable *source, unsigned short setPrimaryKeys) { + SymList *orderedColumns = SymList_new(NULL); + SymIterator *iter = source->columns->iterator(source->columns); + while (iter->has_next(iter)) { + SymColumn *srcColumn = (SymColumn *) iter->next(iter); + SymColumn *column = this->find_column(this, srcColumn->name, 0); + if (column) { + // TODO: clone the SymColumn? + orderedColumns->add(orderedColumns, column); + if (setPrimaryKeys) { + column->isPrimaryKey = srcColumn->isPrimaryKey; + } + } + } + iter->destroy(iter); + + SymTable *copy = SymTable_new(NULL); + copy->catalog = SymStringBuilder_copy(this->catalog); + copy->schema = SymStringBuilder_copy(this->schema); + copy->name = SymStringBuilder_copy(this->name); + copy->columns = orderedColumns; + return copy; +} + +void SymTable_copy_column_types_from(SymTable *this, SymTable *source) { + SymIterator *srcIter = source->columns->iterator(source->columns); + while (srcIter->has_next(srcIter)) { + SymColumn *srcColumn = (SymColumn *) srcIter->next(srcIter); + SymColumn *column = this->find_column(this, srcColumn->name, 0); + if (column) { + column->sqlType = srcColumn->sqlType; + } + } + srcIter->destroy(srcIter); +} + +char * SymTable_to_string(SymTable *this) { + SymStringBuilder *sb = SymStringBuilder_new_with_string("Table [name="); + sb->append(sb, this->name); + sb->append(sb, "; catalog=")->append(sb, this->catalog); + sb->append(sb, "; schema=")->append(sb, this->schema); + sb->append(sb, "] columns:"); + if (this->columns) { + SymIterator *iter = this->columns->iterator(this->columns); + while (iter->has_next(iter)) { + SymColumn *column = (SymColumn *) iter->next(iter); + char *toString = column->to_string(column); + sb->append(sb, " ")->append(sb, toString); + free(toString); + } + iter->destroy(iter); + } + return sb->destroy_and_return(sb); } void SymTable_destroy(SymTable *this) { free(this->name); free(this->catalog); free(this->schema); - SymArrayBuilder_destroy_array(this->columns, this->sizeColumns); - SymArrayBuilder_destroy_array(this->keys, this->sizeKeys); + this->columns->destroy(this->columns); free(this); } @@ -44,8 +113,24 @@ SymTable * SymTable_new(SymTable *this) { if (this == NULL) { this = (SymTable *) calloc(1, sizeof(SymTable)); } - this->set = (void *) &SymTable_set; - this->set_array = (void *) &SymTable_set_array; + this->copy_and_filter_columns = (void *) &SymTable_copy_and_filter_columns; + this->copy_column_types_from = (void *) &SymTable_copy_column_types_from; + this->find_column = (void *) &SymTable_find_column; + this->to_string = (void *) &SymTable_to_string; this->destroy = (void *) &SymTable_destroy; return this; } + +SymTable * SymTable_new_with_name(SymTable *this, char *name) { + this = SymTable_new(this); + this->name = SymStringBuilder_copy(name); + return this; +} + +SymTable * SymTable_new_with_fullname(SymTable *this, char *catalog, char *schema, char *name) { + this = SymTable_new(this); + this->catalog = SymStringBuilder_copy(catalog); + this->schema = SymStringBuilder_copy(schema); + this->name = SymStringBuilder_copy(name); + return this; +} diff --git a/symmetric-client-clib/src/db/platform/DatabasePlatform.c b/symmetric-client-clib/src/db/platform/DatabasePlatform.c new file mode 100644 index 0000000000..4b0f208cfe --- /dev/null +++ b/symmetric-client-clib/src/db/platform/DatabasePlatform.c @@ -0,0 +1,49 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "db/platform/DatabasePlatform.h" + +SymTable * SymDatabasePlatform_get_table_from_cache(SymDatabasePlatform *this, char *catalog, char *schema, char *tableName, unsigned int forceReread) { + // TODO: need a hash table and caching of Table objects + return this->read_table_from_database(this, catalog, schema, tableName); +} + +SymTable * SymDatabasePlatform_read_table_from_database(SymDatabasePlatform *this, char *catalog, char *schema, char *tableName) { + if (catalog == NULL) { + catalog = this->defaultCatalog; + } + if (schema == NULL) { + schema = this->defaultSchema; + } + SymTable *table = this->ddlReader->read_table(this->ddlReader, catalog, schema, tableName); + return table; +} + +void SymDatabasePlatform_destroy(SymDatabasePlatform *this) { +} + +SymDatabasePlatform * SymDatabasePlatform_new(SymDatabasePlatform *this) { + if (this != NULL) { + this->get_table_from_cache = (void *) &SymDatabasePlatform_get_table_from_cache; + this->read_table_from_database = (void *) &SymDatabasePlatform_read_table_from_database; + this->destroy = (void *) &SymDatabasePlatform_destroy; + } + return this; +} diff --git a/symmetric-client-clib/src/db/DatabasePlatformFactory.c b/symmetric-client-clib/src/db/platform/DatabasePlatformFactory.c similarity index 96% rename from symmetric-client-clib/src/db/DatabasePlatformFactory.c rename to symmetric-client-clib/src/db/platform/DatabasePlatformFactory.c index d73d7ee7d3..c51e0361b3 100644 --- a/symmetric-client-clib/src/db/DatabasePlatformFactory.c +++ b/symmetric-client-clib/src/db/platform/DatabasePlatformFactory.c @@ -18,7 +18,7 @@ * specific language governing permissions and limitations * under the License. */ -#include "db/DatabasePlatformFactory.h" +#include "db/platform/DatabasePlatformFactory.h" SymDatabasePlatform * SymDatabasePlatformFactory_create(SymProperties *properties) { SymDatabasePlatform *platform = NULL; diff --git a/symmetric-client-clib/src/db/platform/DdlReader.c b/symmetric-client-clib/src/db/platform/DdlReader.c new file mode 100644 index 0000000000..4a1b404e4d --- /dev/null +++ b/symmetric-client-clib/src/db/platform/DdlReader.c @@ -0,0 +1,31 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "db/platform/DdlReader.h" + +void SymDdlReader_destroy(SymDdlReader *this) { +} + +SymDdlReader * SymDdlReader_new(SymDdlReader *this) { + if (this != NULL) { + this->destroy = (void *) &SymDdlReader_destroy; + } + return this; +} diff --git a/symmetric-client-clib/src/db/platform/sqlite/SqliteDdlReader.c b/symmetric-client-clib/src/db/platform/sqlite/SqliteDdlReader.c new file mode 100644 index 0000000000..9606ff504a --- /dev/null +++ b/symmetric-client-clib/src/db/platform/sqlite/SqliteDdlReader.c @@ -0,0 +1,96 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "db/platform/sqlite/SqliteDdlReader.h" + +static int SymSqliteDdlReader_to_sql_type(char *type) { + int sqlType; + if (type == NULL) { + type = "TEXT"; + } + if (strncasecmp(type, "INT", 3) == 0) { + sqlType = SYM_SQL_TYPE_INTEGER; + } else if (strncasecmp(type, "NUM", 3) == 0) { + sqlType = SYM_SQL_TYPE_NUMERIC; + } else if (strncasecmp(type, "BLOB", 4) == 0) { + sqlType = SYM_SQL_TYPE_BLOB; + } else if (strncasecmp(type, "CLOB", 4) == 0) { + sqlType = SYM_SQL_TYPE_CLOB; + } else if (strncasecmp(type, "FLOAT", 5) == 0) { + sqlType = SYM_SQL_TYPE_FLOAT; + } else if (strncasecmp(type, "DOUBLE", 6) == 0) { + sqlType = SYM_SQL_TYPE_DOUBLE; + } else if (strncasecmp(type, "REAL", 4) == 0) { + sqlType = SYM_SQL_TYPE_REAL; + } else if (strncasecmp(type, "DECIMAL", 7) == 0) { + sqlType = SYM_SQL_TYPE_DECIMAL; + } else if (strncasecmp(type, "DATE", 4) == 0) { + sqlType = SYM_SQL_TYPE_DATE; + } else if (strncasecmp(type, "TIMESTAMP", 9) == 0) { + sqlType = SYM_SQL_TYPE_TIMESTAMP; + } else if (strncasecmp(type, "TIME", 4) == 0) { + sqlType = SYM_SQL_TYPE_TIME; + } else { + sqlType = SYM_SQL_TYPE_VARCHAR; + } + return sqlType; +} + +static void * SymSqliteDdlReader_column_mapper(SymRow *row) { + char *name = row->get_string(row, "name"); + int isPrimaryKey = row->get_int(row, "pk"); + SymColumn *column = SymColumn_new(NULL, name, isPrimaryKey); + column->isRequired = row->get_int(row, "notnull"); + column->sqlType = SymSqliteDdlReader_to_sql_type(row->get_string(row, "type")); + return column; +} + +SymTable * SymSqliteDdlReader_read_table(SymSqliteDdlReader *this, char *catalog, char *schema, char *tableName) { + SymTable *table = NULL; + SymSqlTemplate *sqlTemplate = this->platform->get_sql_template(this->platform); + SymStringBuilder *sql = SymStringBuilder_new_with_string("pragma table_info("); + sql->append(sql, tableName)->append(sql, ")"); + int error; + SymList *columns = sqlTemplate->query(sqlTemplate, sql->str, NULL, NULL, &error, SymSqliteDdlReader_column_mapper); + if (columns && columns->size > 0) { + table = SymTable_new(NULL); + table->name = tableName; + table->columns = columns; + } + sql->destroy(sql); + return table; +} + +void SymSqliteDdlReader_destroy(SymSqliteDdlReader *this) { + SymDdlReader *super = (SymDdlReader *) this; + super->destroy(super); + free(this); +} + +SymSqliteDdlReader * SymSqliteDdlReader_new(SymSqliteDdlReader *this, SymDatabasePlatform *platform) { + if (this == NULL) { + this = (SymSqliteDdlReader *) calloc(1, sizeof(SymSqliteDdlReader)); + } + SymDdlReader *super = SymDdlReader_new(&this->super); + super->read_table = (void *) &SymSqliteDdlReader_read_table; + this->platform = platform; + this->destroy = (void *) &SymSqliteDdlReader_destroy; + return this; +} diff --git a/symmetric-client-clib/src/db/SqlitePlatform.c b/symmetric-client-clib/src/db/platform/sqlite/SqlitePlatform.c similarity index 80% rename from symmetric-client-clib/src/db/SqlitePlatform.c rename to symmetric-client-clib/src/db/platform/sqlite/SqlitePlatform.c index 537d4d28d2..801d0d40a0 100644 --- a/symmetric-client-clib/src/db/SqlitePlatform.c +++ b/symmetric-client-clib/src/db/platform/sqlite/SqlitePlatform.c @@ -18,7 +18,7 @@ * specific language governing permissions and limitations * under the License. */ -#include "db/SqlitePlatform.h" +#include "db/platform/sqlite/SqlitePlatform.h" static int table_exists_cb(void *exists, int argc, char **argv, char **columName) { *((int *) exists) = argc > 0; @@ -41,14 +41,16 @@ int SymSqlitePlatform_execute_sql(SymDatabasePlatform *super, return sqlite3_exec(this->db, sql, callback, arg, errorMessage); } -void SymSqlitePlatform_free(void *data) { - sqlite3_free(data); +SymSqliteSqlTemplate * SymSqlitePlatform_get_sql_template(SymSqlitePlatform *this) { + return (SymSqliteSqlTemplate *) this->sqlTemplate; } void SymSqlitePlatform_destroy(SymDatabasePlatform *super) { printf("Closing SQLite database\n"); SymSqlitePlatform *this = (SymSqlitePlatform *) super; sqlite3_close(this->db); + this->sqlTemplate->destroy(this->sqlTemplate); + free(super->ddlReader); free(this); } @@ -56,13 +58,16 @@ SymSqlitePlatform * SymSqlitePlatform_new(SymSqlitePlatform *this, SymProperties if (this == NULL) { this = (SymSqlitePlatform *) calloc(1, sizeof(SymSqlitePlatform)); } - SymDatabasePlatform_new(&this->super, properties); - SymDatabasePlatform *super = &this->super; + SymDatabasePlatform *super = SymDatabasePlatform_new(&this->super); + super->databaseInfo.catalogSeparator = "."; + super->databaseInfo.schemaSeparator = "."; + super->databaseInfo.delimiterToken = "\""; + super->ddlReader = (SymDdlReader *) SymSqliteDdlReader_new(NULL, (SymDatabasePlatform *) this); super->name = SYM_DATABASE_SQLITE; super->version = (char *) sqlite3_libversion(); super->execute_sql = (void *) &SymSqlitePlatform_execute_sql; super->table_exists = (void *) &SymSqlitePlatform_table_exists; - super->free = (void *) &SymSqlitePlatform_free; + super->get_sql_template = (void *) &SymSqlitePlatform_get_sql_template; super->destroy = (void *) &SymSqlitePlatform_destroy; printf("The IDatabasePlatform being used is SymSqlitePlatform\n"); @@ -79,6 +84,7 @@ SymSqlitePlatform * SymSqlitePlatform_new(SymSqlitePlatform *this, SymProperties sqlite3_close(this->db); return NULL; } + this->sqlTemplate = (SymSqlTemplate *) SymSqliteSqlTemplate_new(NULL, this->db); printf("Detected database '%s', version '%s'\n", super->name, super->version); diff --git a/symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTemplate.c b/symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTemplate.c new file mode 100644 index 0000000000..4d4a20d941 --- /dev/null +++ b/symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTemplate.c @@ -0,0 +1,188 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include + +SymList * SymSqliteSqlTemplate_query(SymSqliteSqlTemplate *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error, void *map_row(SymRow *row)) { + sqlite3_stmt *stmt; + printf("Preparing %s\n", sql); + int rc = sqlite3_prepare_v2(this->db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Failed to prepare query: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + + // TODO: do we need to convert to sqlType and bind correctly? + printf("Binding ["); + int i; + for (i = 0; args != NULL && i < args->size; i++) { + //if (argLengths != NULL) { + // sqlite3_bind_text(stmt, i + 1, args[i], argLengths[i], SQLITE_STATIC); + //} else { + sqlite3_bind_text(stmt, i + 1, args->get(args, i), -1, SQLITE_STATIC); + + if (i > 0) { + printf(","); + } + printf("%s", args->get(args, i)); + } + printf("]\n"); + + SymList *list = SymList_new(NULL); + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + int i, type, size, columnCount = sqlite3_column_count(stmt); + SymRow *row = SymRow_new(NULL, columnCount); + char *name, *value; + for (i = 0; i < columnCount; i++) { + name = (char *) sqlite3_column_name(stmt, i); + type = sqlite3_column_type(stmt, i); + size = sqlite3_column_bytes(stmt, i) + 1; + value = (char *) sqlite3_column_text(stmt, i); + row->put(row, name, value, type, size); + } + void *object = map_row(row); + list->add(list, object); + // TODO: need to destroy each row, and the row mapper needs to make a copy + //row->destroy(row); + } + + if (rc != SQLITE_DONE) { + fprintf(stderr, "Failed to execute query: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + sqlite3_finalize(stmt); + *error = 0; + return list; +} + +static SymRow * SymSqliteSqlTemplate_row_mapper(SymRow *row) { + return row; +} + +SymRow * SymSqliteSqlTemplate_query_for_list(SymSqliteSqlTemplate *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + return (SymRow *) SymSqliteSqlTemplate_query(this, sql, args, sqlTypes, error, (void *) SymSqliteSqlTemplate_row_mapper); +} + +int SymSqliteSqlTemplate_query_for_int(SymSqliteSqlTemplate *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + sqlite3_stmt *stmt; + int rc = sqlite3_prepare_v2(this->db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Failed to prepare query: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + + int i; + for (i = 0; args != NULL && i < args->size; i++) { + //if (argLengths != NULL) { + // sqlite3_bind_text(stmt, i + 1, args[i], argLengths[i], SQLITE_STATIC); + //} else { + sqlite3_bind_text(stmt, i + 1, args->get(args, i), -1, SQLITE_STATIC); + } + + int value = 0; + if ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + value = sqlite3_column_int(stmt, 0); + } else { + fprintf(stderr, "Failed to execute query: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + sqlite3_finalize(stmt); + *error = 0; + return value; +} + +char * SymSqliteSqlTemplate_query_for_string(SymSqliteSqlTemplate *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + sqlite3_stmt *stmt; + int rc = sqlite3_prepare_v2(this->db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Failed to prepare query: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + + int i; + for (i = 0; args != NULL && i < args->size; i++) { + //if (argLengths != NULL) { + // sqlite3_bind_text(stmt, i + 1, args[i], argLengths[i], SQLITE_STATIC); + //} else { + sqlite3_bind_text(stmt, i + 1, args->get(args, i), -1, SQLITE_STATIC); + } + + char *value = NULL; + if ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + value = (char *) sqlite3_column_text(stmt, 0); + } else { + fprintf(stderr, "Failed to execute query: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + sqlite3_finalize(stmt); + *error = 0; + return value; +} + +int SymSqliteSqlTemplate_update(SymSqliteSqlTemplate *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + sqlite3_stmt *stmt; + printf("Executing: %s\n", sql); + int rc = sqlite3_prepare_v2(this->db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Failed to prepare statement: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + + int i; + for (i = 0; args != NULL && i < args->size; i++) { + //if (argLengths != NULL) { + // sqlite3_bind_text(stmt, i + 1, args[i], argLengths[i], SQLITE_STATIC); + //} else { + sqlite3_bind_text(stmt, i + 1, args->get(args, i), -1, SQLITE_STATIC); + } + + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + fprintf(stderr, "Failed to execute statement: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + sqlite3_finalize(stmt); + *error = 0; + return sqlite3_changes(this->db); +} + +SymSqlTransaction * SymSqliteSqlTemplate_start_sql_transaction(SymSqliteSqlTemplate *this) { + return (SymSqlTransaction *) SymSqliteSqlTransaction_new(NULL, this); +} + +void SymSqliteSqlTemplate_destroy(SymSqliteSqlTemplate *this) { + free(this); +} + +SymSqliteSqlTemplate * SymSqliteSqlTemplate_new(SymSqliteSqlTemplate *this, sqlite3 *db) { + if (this == NULL) { + this = (SymSqliteSqlTemplate *) calloc(1, sizeof(SymSqliteSqlTemplate)); + } + this->db = db; + SymSqlTemplate *super = (SymSqlTemplate *) this; + super->query_for_int = (void *) &SymSqliteSqlTemplate_query_for_int; + super->query_for_string = (void *) &SymSqliteSqlTemplate_query_for_string; + super->query_for_list = (void *) &SymSqliteSqlTemplate_query_for_list; + super->query = (void *) &SymSqliteSqlTemplate_query; + super->update = (void *) &SymSqliteSqlTemplate_update; + super->start_sql_transaction = (void *) &SymSqliteSqlTemplate_start_sql_transaction; + this->destroy = (void *) &SymSqliteSqlTemplate_destroy; + return this; +} diff --git a/symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTransaction.c b/symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTransaction.c new file mode 100644 index 0000000000..3729842af9 --- /dev/null +++ b/symmetric-client-clib/src/db/platform/sqlite/SqliteSqlTransaction.c @@ -0,0 +1,128 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include + +int SymSqliteSqlTransaction_query_for_int(SymSqliteSqlTransaction *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + return this->sqlTemplate->query_for_int(this->sqlTemplate, sql, args, sqlTypes, error); +} + +char * SymSqliteSqlTransaction_query_for_string(SymSqliteSqlTransaction *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + return this->sqlTemplate->query_for_string(this->sqlTemplate, sql, args, sqlTypes, error); +} + +void SymSqliteSqlTransaction_query(SymSqliteSqlTransaction *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error, void *callback) { + this->sqlTemplate->query(this->sqlTemplate, sql, args, sqlTypes, error, callback); +} + +static void SymSqliteSqlTransaction_requireTransaction(SymSqliteSqlTransaction *this) { + if (!this->inTransaction) { + sqlite3_exec(this->db, "BEGIN TRANSACTION", NULL, NULL, NULL); + this->inTransaction = 1; + } +} + +void SymSqliteSqlTransaction_prepare(SymSqliteSqlTransaction *this, char *sql) { + this->sql = sql; + SymSqliteSqlTransaction_requireTransaction(this); + printf("Preparing %s\n", sql); + int rc = sqlite3_prepare_v2(this->db, sql, -1, &this->stmt, NULL); + + if (rc != SQLITE_OK) { + fprintf(stderr, "Failed to prepare statement: %s\n", sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } +} + +int SymSqliteSqlTransaction_add_row(SymSqliteSqlTransaction *this, SymStringArray *args, SymList *sqlTypes) { + // TODO: do we need to convert to sqlType and bind correctly? + + sqlite3_reset(this->stmt); + printf("Add Row ["); + int i; + for (i = 0; args != NULL && i < args->size; i++) { + char *arg = args->get(args, i); + if (arg) { + sqlite3_bind_text(this->stmt, i + 1, arg, -1, SQLITE_STATIC); + } else { + sqlite3_bind_null(this->stmt, i + 1); + } + + if (i > 0) { + printf(","); + } + printf("%s", arg); + } + printf("]\n"); + + int rc = sqlite3_step(this->stmt); + if (rc != SQLITE_DONE) { + fprintf(stderr, "Failed to execute statement: %s\n", this->sql); + fprintf(stderr, "SQL Exception: %s\n", sqlite3_errmsg(this->db)); + } + return sqlite3_changes(this->db); +} + +int SymSqliteSqlTransaction_update(SymSqliteSqlTransaction *this, char *sql, SymStringArray *args, SymList *sqlTypes, int *error) { + SymSqliteSqlTransaction_requireTransaction(this); + return this->sqlTemplate->update(this->sqlTemplate, sql, args, sqlTypes, error); +} + +void SymSqliteSqlTransaction_commit(SymSqliteSqlTransaction *this) { + sqlite3_exec(this->db, "COMMIT", NULL, NULL, NULL); + this->inTransaction = 0; +} + +void SymSqliteSqlTransaction_rollback(SymSqliteSqlTransaction *this) { + sqlite3_exec(this->db, "ROLLBACK", NULL, NULL, NULL); + this->inTransaction = 0; +} + +void SymSqliteSqlTransaction_close(SymSqliteSqlTransaction *this) { + if (this->stmt != NULL) { + sqlite3_finalize(this->stmt); + } + this->stmt = NULL; +} + +void SymSqliteSqlTransaction_destroy(SymSqliteSqlTransaction *this) { + SymSqliteSqlTransaction_close(this); + free(this); +} + +SymSqliteSqlTransaction * SymSqliteSqlTransaction_new(SymSqliteSqlTransaction *this, SymSqliteSqlTemplate *sqlTemplate) { + if (this == NULL) { + this = (SymSqliteSqlTransaction *) calloc(1, sizeof(SymSqliteSqlTransaction)); + } + this->sqlTemplate = (SymSqlTemplate *) sqlTemplate; + this->db = sqlTemplate->db; + SymSqlTransaction *super = (SymSqlTransaction *) this; + super->query_for_int = (void *) &SymSqliteSqlTransaction_query_for_int; + super->query_for_string = (void *) &SymSqliteSqlTransaction_query_for_string; + super->query = (void *) &SymSqliteSqlTransaction_query; + super->update = (void *) &SymSqliteSqlTransaction_update; + super->prepare = (void *) &SymSqliteSqlTransaction_prepare; + super->add_row = (void *) &SymSqliteSqlTransaction_add_row; + super->commit = (void *) &SymSqliteSqlTransaction_commit; + super->rollback = (void *) &SymSqliteSqlTransaction_rollback; + super->close = (void *) &SymSqliteSqlTransaction_close; + this->destroy = (void *) &SymSqliteSqlTransaction_destroy; + return this; +} diff --git a/symmetric-client-clib/src/db/sql/DmlStatement.c b/symmetric-client-clib/src/db/sql/DmlStatement.c new file mode 100644 index 0000000000..125511b859 --- /dev/null +++ b/symmetric-client-clib/src/db/sql/DmlStatement.c @@ -0,0 +1,178 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "db/sql/DmlStatement.h" + +static void append_table_name(SymDmlStatement *this, SymStringBuilder *sb) { + sb->append(sb, SymTable_get_full_table_name(this->table, this->databaseInfo->delimiterToken, this->databaseInfo->catalogSeparator, + this->databaseInfo->schemaSeparator)); +} + +static void append_columns(SymDmlStatement *this, SymStringBuilder *sb) { + SymIterator *iter = this->table->columns->iterator(this->table->columns); + while (iter->has_next(iter)) { + SymColumn *column = (SymColumn *) iter->next(iter); + if (iter->index > 0) { + sb->append(sb, ", "); + } + sb->append(sb, this->databaseInfo->delimiterToken); + sb->append(sb, column->name); + sb->append(sb, this->databaseInfo->delimiterToken); + } + iter->destroy(iter); +} + +static void append_column_questions(SymDmlStatement *this, SymStringBuilder *sb) { + SymIterator *iter = this->table->columns->iterator(this->table->columns); + while (iter->has_next(iter)) { + iter->next(iter); + if (iter->index > 0) { + sb->append(sb, ", "); + } + sb->append(sb, "?"); + } + iter->destroy(iter); +} + +static void build_insert(SymDmlStatement *this) { + SymStringBuilder *sb = SymStringBuilder_new_with_string("insert into "); + append_table_name(this, sb); + sb->append(sb, " ("); + append_columns(this, sb); + sb->append(sb, ") values ("); + append_column_questions(this, sb); + sb->append(sb, ")"); + this->sql = sb->destroy_and_return(sb); +} + +static void append_columns_equals_set(SymDmlStatement *this, SymStringBuilder *sb) { + SymIterator *iter = this->table->columns->iterator(this->table->columns); + while (iter->has_next(iter)) { + SymColumn *column = (SymColumn *) iter->next(iter); + if (iter->index > 0) { + sb->append(sb, ", "); + } + sb->append(sb, this->databaseInfo->delimiterToken); + sb->append(sb, column->name); + sb->append(sb, this->databaseInfo->delimiterToken); + sb->append(sb, " = ?"); + } + iter->destroy(iter); +} + +static void append_columns_equals(SymDmlStatement *this, SymStringBuilder *sb) { + SymIterator *iter = this->table->columns->iterator(this->table->columns); + while (iter->has_next(iter)) { + SymColumn *column = (SymColumn *) iter->next(iter); + if (iter->index > 0) { + sb->append(sb, " and "); + } + sb->append(sb, this->databaseInfo->delimiterToken); + sb->append(sb, column->name); + sb->append(sb, this->databaseInfo->delimiterToken); + if (this->nullKeyIndicators != NULL && this->nullKeyIndicators->get(this->nullKeyIndicators, iter->index)) { + sb->append(sb, " is NULL"); + } else { + sb->append(sb, " = ?"); + } + } + iter->destroy(iter); +} + +static void build_update(SymDmlStatement *this) { + SymStringBuilder *sb = SymStringBuilder_new_with_string("update "); + append_table_name(this, sb); + sb->append(sb, " set "); + append_columns_equals_set(this, sb); + sb->append(sb, " where "); + append_columns_equals(this, sb); + this->sql = sb->destroy_and_return(sb); +} + +static void build_delete(SymDmlStatement *this) { + SymStringBuilder *sb = SymStringBuilder_new_with_string("delete from "); + append_table_name(this, sb); + sb->append(sb, " where "); + append_columns_equals(this, sb); + this->sql = sb->destroy_and_return(sb); +} + +static void build_select(SymDmlStatement *this) { + SymStringBuilder *sb = SymStringBuilder_new_with_string("select "); + append_columns(this, sb); + sb->append(sb, " from "); + append_table_name(this, sb); + sb->append(sb, " where "); + append_columns_equals(this, sb); + this->sql = sb->destroy_and_return(sb); +} + +static void build_sql_types_list(SymList *sqlTypes, SymList *columns, int isPrimaryKey) { + SymIterator *iter = columns->iterator(columns); + while (iter->has_next(iter)) { + SymColumn *column = (SymColumn *) iter->next(iter); + if (column->isPrimaryKey == isPrimaryKey) { + columns->add(columns, &column->sqlType); + } + } + iter->destroy(iter); +} + +static void build_sql_types(SymDmlStatement *this, SymDmlType dmlType, SymTable *table) { + this->sqlTypes = SymList_new(NULL); + if (dmlType == SYM_DML_TYPE_INSERT) { + build_sql_types_list(this->sqlTypes, table->columns, -1); + } else if (dmlType == SYM_DML_TYPE_UPDATE) { + build_sql_types_list(this->sqlTypes, table->columns, 0); + build_sql_types_list(this->sqlTypes, table->columns, 1); + } else if (dmlType == SYM_DML_TYPE_DELETE) { + build_sql_types_list(this->sqlTypes, table->columns, 1); + } +} + +void SymDmlStatement_destroy(SymDmlStatement *this) { + free(this->sql); + free(this); +} + +SymDmlStatement * SymDmlStatement_new(SymDmlStatement *this, SymDmlType dmlType, SymTable *table, SymList *nullKeyIndicators, + SymDatabaseInfo *databaseInfo) { + if (this == NULL) { + this = (SymDmlStatement *) calloc(1, sizeof(SymDmlStatement)); + } + this->dmlType = dmlType; + this->table = table; + this->databaseInfo = databaseInfo; + this->nullKeyIndicators = nullKeyIndicators; + this->destroy = (void *) &SymDmlStatement_destroy; + + build_sql_types(this, dmlType, table); + if (dmlType == SYM_DML_TYPE_INSERT) { + build_insert(this); + } else if (dmlType == SYM_DML_TYPE_UPDATE) { + build_update(this); + } else if (dmlType == SYM_DML_TYPE_DELETE) { + build_delete(this); + } else if (dmlType == SYM_DML_TYPE_SELECT) { + build_select(this); + } + + return this; +} diff --git a/symmetric-client-clib/src/db/sql/Row.c b/symmetric-client-clib/src/db/sql/Row.c new file mode 100644 index 0000000000..ba7ab13fd0 --- /dev/null +++ b/symmetric-client-clib/src/db/sql/Row.c @@ -0,0 +1,95 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "db/sql/Row.h" + +void SymRow_put(SymRow *this, char *columnName, void *value, int sqlType, int size) { + char *copyValue = NULL; + if (value != NULL) { + copyValue = (char *) memcpy(malloc(size + 1), value, size); + copyValue[size] = '\0'; + } + SymRowEntry entry = { copyValue, sqlType, size }; + this->map->put(this->map, columnName, &entry, sizeof(SymRowEntry)); +} + +char * SymRow_get_string(SymRow *this, char *columnName) { + char *value = NULL; + SymRowEntry *entry = (SymRowEntry *) this->map->get(this->map, columnName); + if (entry != NULL) { + value = entry->value; + } + return value; +} + +int SymRow_get_int(SymRow *this, char *columnName) { + int value = 0; + char *str = SymRow_get_string(this, columnName); + if (str != NULL) { + value = atoi(str); + } + return value; +} + +int SymRow_get_size(SymRow *this, char *columnName) { + int value = 0; + SymRowEntry *entry = (SymRowEntry *) this->map->get(this->map, columnName); + if (entry != NULL) { + value = entry->size; + } + return value; +} + +int SymRow_get_sql_type(SymRow *this, char *columnName) { + int value = 0; + SymRowEntry *entry = (SymRowEntry *) this->map->get(this->map, columnName); + if (entry != NULL) { + value = entry->sqlType; + } + return value; +} + +void SymRow_destroy(SymRow *this) { + /* + int i; + for (i = 0; i < this->map->size; i++) { + if (this->map->table[i]) { + SymRowEntry *entry = (SymRowEntry *) this->map->table[i]->value; + free(entry->value); + } + } + */ + this->map->destroy(this->map); + free(this); +} + +SymRow * SymRow_new(SymRow *this, int columnCount) { + if (this == NULL) { + this = (SymRow *) calloc(1, sizeof(SymRow)); + } + this->map = SymMap_new(NULL, columnCount); + this->put = (void *) &SymRow_put; + this->get_int = (void *) &SymRow_get_int; + this->get_string = (void *) &SymRow_get_string; + this->get_size = (void *) &SymRow_get_size; + this->get_sql_type = (void *) &SymRow_get_sql_type; + this->destroy = (void *) &SymRow_destroy; + return this; +} diff --git a/symmetric-client-clib/src/db/SqliteDialect.c b/symmetric-client-clib/src/db/sqlite/SqliteDialect.c similarity index 95% rename from symmetric-client-clib/src/db/SqliteDialect.c rename to symmetric-client-clib/src/db/sqlite/SqliteDialect.c index 14b8d30758..d877ac359b 100644 --- a/symmetric-client-clib/src/db/SqliteDialect.c +++ b/symmetric-client-clib/src/db/sqlite/SqliteDialect.c @@ -18,15 +18,16 @@ * specific language governing permissions and limitations * under the License. */ -#include "db/SqliteDialect.h" +#include "db/sqlite/SqliteDialect.h" static int create_if_missing(SymDialect *super, char *tableName, char *createSql) { + // TODO: re-implement this using ddl reader if (!super->platform->table_exists(super->platform, tableName)) { char *errorMessage; printf("DDL applied: %s\n", tableName); if (super->platform->execute_sql(super->platform, createSql, NULL, NULL, &errorMessage)) { fprintf(stderr, "Error creating %s table: %s\n", tableName, errorMessage); - super->platform->free(errorMessage); + free(errorMessage); return 1; } } @@ -40,6 +41,7 @@ int SymSqliteDialect_init_tables(SymDialect *super) { create_if_missing(super, "sym_data_event", CREATE_SYM_DATA_EVENT); create_if_missing(super, "sym_incoming_batch", CREATE_SYM_INCOMING_BATCH); create_if_missing(super, "sym_node", CREATE_SYM_NODE); + create_if_missing(super, "sym_node_security", CREATE_SYM_NODE_SECURITY); create_if_missing(super, "sym_node_group", CREATE_SYM_NODE_GROUP); create_if_missing(super, "sym_node_group_link", CREATE_SYM_NODE_GROUP_LINK); create_if_missing(super, "sym_node_host", CREATE_SYM_NODE_HOST); diff --git a/symmetric-client-clib/src/io/data/Batch.c b/symmetric-client-clib/src/io/data/Batch.c index d555b9d00a..2b37d8f331 100644 --- a/symmetric-client-clib/src/io/data/Batch.c +++ b/symmetric-client-clib/src/io/data/Batch.c @@ -20,11 +20,6 @@ */ #include "io/data/Batch.h" -void SymBatch_set(char **strField, char *str) { - free(*strField); - *strField = SymStringBuilder_copy(str); -} - void SymBatch_destroy(SymBatch *this) { free(this->channelId); free(this->sourceNodeId); @@ -36,7 +31,6 @@ SymBatch * SymBatch_new(SymBatch *this) { if (this == NULL) { this = (SymBatch *) calloc(1, sizeof(SymBatch)); } - this->set = (void *) &SymBatch_set; this->destroy = (void *) &SymBatch_destroy; return this; } diff --git a/symmetric-client-clib/src/io/data/CsvData.c b/symmetric-client-clib/src/io/data/CsvData.c index 2669b3a6d1..0283ec7dfa 100644 --- a/symmetric-client-clib/src/io/data/CsvData.c +++ b/symmetric-client-clib/src/io/data/CsvData.c @@ -20,19 +20,16 @@ */ #include "io/data/CsvData.h" -void SymCsvData_set_array(char ***arrayField, int *sizeField, char **array, int sizeArray) { - SymArrayBuilder_destroy_array(*arrayField, *sizeField); - *arrayField = SymArrayBuilder_copy_array(array, sizeArray); - *sizeField = sizeArray; -} - void SymCsvData_reset(SymCsvData *this) { - SymArrayBuilder_destroy_array(this->rowData, this->sizeRowData); - SymArrayBuilder_destroy_array(this->pkData, this->sizePkData); - SymArrayBuilder_destroy_array(this->oldData, this->sizeOldData); - this->sizeRowData = 0; - this->sizePkData = 0; - this->sizeOldData = 0; + if (this->rowData) { + this->rowData->destroy(this->rowData); + } + if (this->oldData) { + this->oldData->destroy(this->oldData); + } + if (this->pkData) { + this->pkData->destroy(this->pkData); + } } void SymCsvData_destroy(SymCsvData *this) { @@ -44,17 +41,7 @@ SymCsvData * SymCsvData_new(SymCsvData *this) { if (this == NULL) { this = (SymCsvData *) calloc(1, sizeof(SymCsvData)); } - this->set_array = (void *) &SymCsvData_set_array; this->reset = (void *) &SymCsvData_reset; this->destroy = (void *) &SymCsvData_destroy; return this; } - -SymCsvData * SymCsvData_new_with_settings(SymCsvData *this, SymDataEventType dataEventType, char **rowData, char **pkData, char **oldData, - int sizeRowData, int sizePkData, int sizeOldData) { - this = SymCsvData_new(this); - this->rowData = SymArrayBuilder_copy_array(rowData, sizeRowData); - this->pkData = SymArrayBuilder_copy_array(pkData, sizePkData); - this->oldData = SymArrayBuilder_copy_array(oldData, sizeOldData); - return this; -} diff --git a/symmetric-client-clib/src/io/reader/ProtocolDataReader.c b/symmetric-client-clib/src/io/reader/ProtocolDataReader.c index e2e0296189..69991191d9 100644 --- a/symmetric-client-clib/src/io/reader/ProtocolDataReader.c +++ b/symmetric-client-clib/src/io/reader/ProtocolDataReader.c @@ -22,73 +22,74 @@ static void SymProtocolDataReader_parse_field(void *data, size_t size, void *userData) { SymProtocolDataReader *this = (SymProtocolDataReader *) userData; - this->fields->addn(this->fields, data, size); + if (!this->isError) { + this->fields->addn(this->fields, data, size); + } } static void SymProtocolDataReader_parse_line(int eol, void *userData) { SymProtocolDataReader *this = (SymProtocolDataReader *) userData; SymBatch *batch = this->batch; - SymTable *table = this->table; - SymCsvData *csvData = this->csvData; - SymArrayBuilder *fields = this->fields; + SymStringArray *fields = this->fields; - if (fields->size > 0) { + if (!this->isError && fields->size > 0) { char *token = fields->get(fields, 0); if (strcmp(token, SYM_CSV_INSERT) == 0) { - char **rowData = fields->to_array_range(fields, 1, fields->size); - int sizeRowData = fields->size - 1; - csvData->reset(csvData); + SymCsvData *csvData = SymCsvData_new(NULL); + csvData->rowData = fields->subarray(fields, 1, fields->size); csvData->dataEventType = SYM_DATA_EVENT_INSERT; - csvData->set_array(&csvData->rowData, &csvData->sizeRowData, rowData, sizeRowData); - this->writer->write(this->writer, csvData); - SymArrayBuilder_destroy_array(rowData, sizeRowData); + this->isError = !this->writer->write(this->writer, csvData); + csvData->destroy(csvData); } else if (strcmp(token, SYM_CSV_OLD) == 0) { - csvData->reset(csvData); - char **oldData = fields->to_array_range(fields, 1, fields->size); - int sizeOldData = fields->size - 1; - csvData->set_array(&csvData->oldData, &csvData->sizeOldData, oldData, sizeOldData); - SymArrayBuilder_destroy_array(oldData, sizeOldData); + if (this->oldData != NULL) { + this->oldData->destroy(this->oldData); + } + this->oldData = fields->subarray(fields, 1, fields->size); } else if (strcmp(token, SYM_CSV_UPDATE) == 0) { - char **rowData = fields->to_array_range(fields, 1, fields->size); - int sizeRowData = fields->size - 1; - char **pkData = fields->to_array_range(fields, 1, table->sizeKeys + 1); - int sizePkData = table->sizeKeys; + SymCsvData *csvData = SymCsvData_new(NULL); + csvData->rowData = fields->subarray(fields, 1, fields->size); + csvData->pkData = fields->subarray(fields, 1, this->keys->size + 1); + csvData->oldData = this->oldData; csvData->dataEventType = SYM_DATA_EVENT_UPDATE; - csvData->set_array(&csvData->rowData, &csvData->sizeRowData, rowData, sizeRowData); - csvData->set_array(&csvData->pkData, &csvData->sizePkData, pkData, sizePkData); - this->writer->write(this->writer, csvData); - SymArrayBuilder_destroy_array(rowData, sizeRowData); - SymArrayBuilder_destroy_array(pkData, sizePkData); + this->isError = !this->writer->write(this->writer, csvData); + csvData->destroy(csvData); + this->oldData = NULL; } else if (strcmp(token, SYM_CSV_DELETE) == 0) { - char **pkData = fields->to_array_range(fields, 1, fields->size); - int sizePkData = fields->size - 1; - csvData->reset(csvData); + SymCsvData *csvData = SymCsvData_new(NULL); + csvData->pkData = fields->subarray(fields, 1, fields->size); csvData->dataEventType = SYM_DATA_EVENT_DELETE; - csvData->set_array(&csvData->pkData, &csvData->sizePkData, pkData, sizePkData); - csvData->sizePkData = sizePkData; - this->writer->write(this->writer, csvData); - SymArrayBuilder_destroy_array(pkData, sizePkData); + this->isError = !this->writer->write(this->writer, csvData); + csvData->destroy(csvData); } else if (strcmp(token, SYM_CSV_CATALOG) == 0) { - table->set(&table->catalog, fields->get(fields, 1)); + SymStringBuilder_copy_to_field(&this->catalog, fields->get(fields, 1)); } else if (strcmp(token, SYM_CSV_SCHEMA) == 0) { - table->set(&table->schema, fields->get(fields, 1)); + SymStringBuilder_copy_to_field(&this->schema, fields->get(fields, 1)); } else if (strcmp(token, SYM_CSV_TABLE) == 0) { - table->set(&table->name, fields->get(fields, 1)); - this->writer->start_table(this->writer, table); + char *tableName = fields->get(fields, 1); + this->table = (SymTable *) this->parsedTables->get(this->parsedTables, tableName); + if (this->table) { + this->writer->start_table(this->writer, this->table); + } else { + this->table = SymTable_new_with_fullname(NULL, this->catalog, this->schema, tableName); + } } else if (strcmp(token, SYM_CSV_KEYS) == 0) { - char **keys = fields->to_array_range(fields, 1, fields->size); - int sizeKeys = fields->size - 1; - table->set_array(&table->keys, &table->sizeKeys, keys, sizeKeys); - SymArrayBuilder_destroy_array(keys, sizeKeys); + this->keys = fields->subarray(fields, 1, fields->size); } else if (strcmp(token, SYM_CSV_COLUMNS) == 0) { - char **columns = fields->to_array_range(fields, 1, fields->size); - int sizeColumns = fields->size - 1; - table->set_array(&table->columns, &table->sizeColumns, columns, sizeColumns); - SymArrayBuilder_destroy_array(columns, sizeColumns); + this->table->columns = SymList_new(NULL); + int i, isPrimary; + for (i = 1; i < this->fields->size; i++) { + isPrimary = this->keys->contains(this->keys, this->fields->array[i]); + this->table->columns->add(this->table->columns, SymColumn_new(NULL, this->fields->array[i], isPrimary)); + } + this->parsedTables->put(this->parsedTables, this->table->name, this->table, sizeof(SymTable)); + //SymTable *poop = (SymTable *) this->parsedTables->get(this->parsedTables, this->table->name); + //printf("%s", poop->to_string(poop)); + //printf("\n"); + this->writer->start_table(this->writer, this->table); } else if (strcmp(token, SYM_CSV_NODEID) == 0) { - batch->set(&batch->sourceNodeId, fields->get(fields, 1)); + SymStringBuilder_copy_to_field(&batch->sourceNodeId, fields->get(fields, 1)); } else if (strcmp(token, SYM_CSV_CHANNEL) == 0) { - batch->set(&batch->channelId, fields->get(fields, 1)); + SymStringBuilder_copy_to_field(&batch->channelId, fields->get(fields, 1)); } else if (strcmp(token, SYM_CSV_BATCH) == 0) { batch->batchId = atol(fields->get(fields, 1)); this->writer->start_batch(this->writer, batch); @@ -97,16 +98,17 @@ static void SymProtocolDataReader_parse_line(int eol, void *userData) { } else if (strcmp(token, SYM_CSV_IGNORE) == 0) { batch->isIgnore = 1; } else if (strcmp(token, SYM_CSV_SQL) == 0) { - char **rowData = fields->to_array_range(fields, 1, fields->size); - int sizeRowData = fields->size - 1; - csvData->reset(csvData); + SymCsvData *csvData = SymCsvData_new(NULL); + csvData->rowData = fields->subarray(fields, 1, fields->size); csvData->dataEventType = SYM_DATA_EVENT_SQL; - csvData->set_array(&csvData->rowData, &csvData->sizeRowData, rowData, sizeRowData); - this->writer->write(this->writer, csvData); - SymArrayBuilder_destroy_array(rowData, sizeRowData); + this->isError = !this->writer->write(this->writer, csvData); + csvData->destroy(csvData); + } + if (this->isError) { + this->writer->end_batch(this->writer, batch); } + fields->reset(fields); } - fields->reset(fields); } void SymProtocolDataReader_open(SymProtocolDataReader *this) { @@ -120,6 +122,9 @@ size_t SymProtocolDataReader_process(SymProtocolDataReader *this, char *data, si fprintf(stderr, "Error from CSV parser: %s\n", csv_strerror(csv_error(this->csvParser))); return 0; } + if (this->isError) { + return 0; + } return length; } @@ -133,8 +138,7 @@ void SymProtocolDataReader_destroy(SymProtocolDataReader *this) { free(this->csvParser); this->fields->destroy(this->fields); this->batch->destroy(this->batch); - this->table->destroy(this->table); - this->csvData->destroy(this->csvData); + this->parsedTables->destroy(this->parsedTables); free(this); } @@ -145,11 +149,10 @@ SymProtocolDataReader * SymProtocolDataReader_new(SymProtocolDataReader *this, c this->targetNodeId = targetNodeId; this->writer = writer; this->csvParser = (struct csv_parser *) calloc(1, sizeof(struct csv_parser)); - this->fields = SymArrayBuilder_new(); + this->fields = SymStringArray_new_with_size(NULL, 1024, 1024); this->batch = SymBatch_new(NULL); - this->batch->set(&(this->batch->targetNodeId), targetNodeId); - this->table = SymTable_new(NULL); - this->csvData = SymCsvData_new(NULL); + this->batch->targetNodeId = SymStringBuilder_copy(targetNodeId); + this->parsedTables = SymMap_new(NULL, 100); SymDataReader *super = &this->super; super->open = (void *) &SymProtocolDataReader_open; diff --git a/symmetric-client-clib/src/io/writer/DefaultDatabaseWriter.c b/symmetric-client-clib/src/io/writer/DefaultDatabaseWriter.c index 1b7c89e274..0779d293e0 100644 --- a/symmetric-client-clib/src/io/writer/DefaultDatabaseWriter.c +++ b/symmetric-client-clib/src/io/writer/DefaultDatabaseWriter.c @@ -22,23 +22,84 @@ void SymDefaultDatabaseWriter_open(SymDefaultDatabaseWriter *this) { printf("open\n"); -} - -void SymDefaultDatabaseWriter_close(SymDefaultDatabaseWriter *this) { - printf("close\n"); + SymSqlTemplate *sqlTemplate = this->platform->get_sql_template(this->platform); + this->sqlTransaction = sqlTemplate->start_sql_transaction(sqlTemplate); } void SymDefaultDatabaseWriter_start_batch(SymDefaultDatabaseWriter *this, SymBatch *batch) { printf("start batch %ld\n", batch->batchId); + this->batch = batch; + this->isError = 0; } -int SymDefaultDatabaseWriter_start_table(SymDefaultDatabaseWriter *this, SymTable *table) { +unsigned short SymDefaultDatabaseWriter_start_table(SymDefaultDatabaseWriter *this, SymTable *table) { printf("start table %s\n", table->name); - return 0; + this->dmlStatement = NULL; + this->sourceTable = table; + + SymTable *targetTable = this->platform->get_table_from_cache(this->platform, table->catalog, table->schema, table->name, 0); + + if (targetTable) { + // TODO: cache the filtered table + this->targetTable = targetTable->copy_and_filter_columns(targetTable, table, 1); + } else { + this->targetTable = table; + } + return 1; } -void SymDefaultDatabaseWriter_write(SymDefaultDatabaseWriter *this, SymCsvData *data) { - SymArrayBuilder_print_array(data->rowData, data->sizeRowData); +unsigned short SymDefaultDatabaseWriter_requires_new_statement(SymDefaultDatabaseWriter *this, SymDmlType currentDmlType, SymCsvData *data) { + unsigned short requiresNew = this->dmlStatement == NULL || this->dmlStatement->dmlType != currentDmlType; + return requiresNew; +} + +void SymDefaultDatabaseWriter_insert(SymDefaultDatabaseWriter *this, SymCsvData *data) { + printf("insert"); + if (SymDefaultDatabaseWriter_requires_new_statement(this, SYM_DML_TYPE_INSERT, data)) { + if (this->dmlStatement) { + this->sqlTransaction->close(this->sqlTransaction); + this->dmlStatement->destroy(this->dmlStatement); + } + this->dmlStatement = SymDmlStatement_new(NULL, SYM_DML_TYPE_INSERT, this->targetTable, NULL, &this->platform->databaseInfo); + this->sqlTransaction->prepare(this->sqlTransaction, this->dmlStatement->sql); + } + // TODO: need to know length of each rowData + this->sqlTransaction->add_row(this->sqlTransaction, data->rowData, this->dmlStatement->sqlTypes); +} + +void SymDefaultDatabaseWriter_update(SymDefaultDatabaseWriter *this, SymCsvData *data) { + +} + +void SymDefaultDatabaseWriter_delete(SymDefaultDatabaseWriter *this, SymCsvData *data) { + +} + +void SymDefaultDatabaseWriter_sql(SymDefaultDatabaseWriter *this, SymCsvData *data) { + if (this->dmlStatement) { + this->sqlTransaction->close(this->sqlTransaction); + this->dmlStatement->destroy(this->dmlStatement); + } + int error; + this->sqlTransaction->update(this->sqlTransaction, data->rowData->array[0], NULL, NULL, &error); +} + +unsigned short SymDefaultDatabaseWriter_write(SymDefaultDatabaseWriter *this, SymCsvData *data) { + switch (data->dataEventType) { + case SYM_DATA_EVENT_INSERT: + SymDefaultDatabaseWriter_insert(this, data); + break; + case SYM_DATA_EVENT_UPDATE: + SymDefaultDatabaseWriter_update(this, data); + break; + case SYM_DATA_EVENT_DELETE: + SymDefaultDatabaseWriter_delete(this, data); + break; + case SYM_DATA_EVENT_SQL: + SymDefaultDatabaseWriter_sql(this, data); + break; + } + return 1; } void SymDefaultDatabaseWriter_end_table(SymDefaultDatabaseWriter *this, SymTable *table) { @@ -47,24 +108,36 @@ void SymDefaultDatabaseWriter_end_table(SymDefaultDatabaseWriter *this, SymTable void SymDefaultDatabaseWriter_end_batch(SymDefaultDatabaseWriter *this, SymBatch *batch) { printf("end batch %ld\n", batch->batchId); + this->dmlStatement = NULL; + if (!this->isError) { + this->sqlTransaction->commit(this->sqlTransaction); + } else { + this->sqlTransaction->rollback(this->sqlTransaction); + } +} + +void SymDefaultDatabaseWriter_close(SymDefaultDatabaseWriter *this) { + printf("close\n"); + this->sqlTransaction->close(this->sqlTransaction); } void SymDefaultDatabaseWriter_destroy(SymDefaultDatabaseWriter *this) { free(this); } -SymDefaultDatabaseWriter * SymDefaultDatabaseWriter_new(SymDefaultDatabaseWriter *this) { +SymDefaultDatabaseWriter * SymDefaultDatabaseWriter_new(SymDefaultDatabaseWriter *this, SymDatabasePlatform *platform) { if (this == NULL) { this = (SymDefaultDatabaseWriter *) calloc(1, sizeof(SymDefaultDatabaseWriter)); } SymDataWriter *super = &this->super; + this->platform = platform; super->open = (void *) &SymDefaultDatabaseWriter_open; - super->close = (void *) &SymDefaultDatabaseWriter_close; super->start_batch = (void *) &SymDefaultDatabaseWriter_start_batch; super->start_table = (void *) &SymDefaultDatabaseWriter_start_table; super->write = (void *) &SymDefaultDatabaseWriter_write; super->end_table = (void *) &SymDefaultDatabaseWriter_end_table; super->end_batch = (void *) &SymDefaultDatabaseWriter_end_batch; + super->close = (void *) &SymDefaultDatabaseWriter_close; super->destroy = (void *) &SymDefaultDatabaseWriter_destroy; return this; } diff --git a/symmetric-client-clib/src/model/Node.c b/symmetric-client-clib/src/model/Node.c index b373af45e1..2b44c7570d 100644 --- a/symmetric-client-clib/src/model/Node.c +++ b/symmetric-client-clib/src/model/Node.c @@ -28,6 +28,7 @@ SymNode * SymNode_new(SymNode *this) { if (this == NULL) { this = (SymNode *) calloc(1, sizeof(SymNode)); } + this->symmetricVersion = SYM_VERSION; this->destroy = (void *) &SymNode_destroy; return this; } diff --git a/symmetric-client-clib/src/service/DataLoaderService.c b/symmetric-client-clib/src/service/DataLoaderService.c index a6c1f19af8..a473144192 100644 --- a/symmetric-client-clib/src/service/DataLoaderService.c +++ b/symmetric-client-clib/src/service/DataLoaderService.c @@ -43,7 +43,7 @@ static void send_ack(SymDataLoaderService *this, SymNode *remote, SymNode *local static SymIncomingBatch ** load_data_from_transport(SymDataLoaderService *this, SymNode *remote, SymIncomingTransport *transport, int *error) { // TODO: - SymDataWriter *writer = (SymDataWriter *) SymDefaultDatabaseWriter_new(NULL); + SymDataWriter *writer = (SymDataWriter *) SymDefaultDatabaseWriter_new(NULL, this->platform); SymDataReader *reader = (SymDataReader *) SymProtocolDataReader_new(NULL, remote->nodeId, writer); long rc = transport->process(transport, reader); diff --git a/symmetric-client-clib/src/service/PushService.c b/symmetric-client-clib/src/service/PushService.c index 1c2f9d4aa8..f6eb4234d8 100644 --- a/symmetric-client-clib/src/service/PushService.c +++ b/symmetric-client-clib/src/service/PushService.c @@ -21,8 +21,7 @@ #include "service/PushService.h" SymRemoteNodeStatus * SymPushService_push_data(SymPushService *this) { - SymRemoteNodeStatus remoteNodeStatuses[0]; - return remoteNodeStatuses; + return NULL; } void SymPushService_destroy(SymPushService *this) { diff --git a/symmetric-client-clib/src/transport/http/HttpTransportManager.c b/symmetric-client-clib/src/transport/http/HttpTransportManager.c index 4a13331ddc..0b250caff7 100644 --- a/symmetric-client-clib/src/transport/http/HttpTransportManager.c +++ b/symmetric-client-clib/src/transport/http/HttpTransportManager.c @@ -21,7 +21,7 @@ #include "transport/http/HttpTransportManager.h" static void append(SymStringBuilder *sb, char *name, char *value) { - if (strstr(sb->to_string(sb), SYM_WEB_CONSTANTS_QUERY) == NULL) { + if (strstr(sb->str, SYM_WEB_CONSTANTS_QUERY) == NULL) { sb->append(sb, SYM_WEB_CONSTANTS_QUERY); } else { sb->append(sb, SYM_WEB_CONSTANTS_AND); @@ -42,6 +42,7 @@ static char * build_url(char *action, SymNode *remote, SymNode *local, char *sec sb->append(sb, action); append(sb, SYM_WEB_CONSTANTS_NODE_ID, local->nodeId); append(sb, SYM_WEB_CONSTANTS_SECURITY_TOKEN, securityToken); + // TODO: get hostname/ip address //append(sb, SYM_WEB_CONSTANTS_HOST_NAME, "todo-host"); //append(sb, SYM_WEB_CONSTANTS_IP_ADDRESS, "todo-ipapddr"); return sb->destroy_and_return(sb); @@ -57,6 +58,7 @@ static char * build_registration_url(SymNode *local, char *registrationUrl) { append(sb, SYM_WEB_CONSTANTS_DATABASE_TYPE, local->databaseType); append(sb, SYM_WEB_CONSTANTS_DATABASE_VERSION, local->databaseVersion); append(sb, SYM_WEB_CONSTANTS_SYMMETRIC_VERSION, local->symmetricVersion); + // TODO: get hostname/ip address //append(sb, SYM_WEB_CONSTANTS_HOST_NAME, "todo-host"); //append(sb, SYM_WEB_CONSTANTS_IP_ADDRESS, "todo-ipaddr"); return sb->destroy_and_return(sb); diff --git a/symmetric-client-clib/src/util/ArrayBuilder.c b/symmetric-client-clib/src/util/ArrayBuilder.c deleted file mode 100644 index 53d869c160..0000000000 --- a/symmetric-client-clib/src/util/ArrayBuilder.c +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Licensed to JumpMind Inc under one or more contributor - * license agreements. See the NOTICE file distributed - * with this work for additional information regarding - * copyright ownership. JumpMind Inc licenses this file - * to you under the GNU General Public License, version 3.0 (GPLv3) - * (the "License"); you may not use this file except in compliance - * with the License. - * - * You should have received a copy of the GNU General Public License, - * version 3.0 (GPLv3) along with this library; if not, see - * . - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#include "util/ArrayBuilder.h" - -void SymArrayBuilder_add(SymArrayBuilder *this, char *src) { - this->addn(this, src, strlen(src)); -} - -void SymArrayBuilder_addn(SymArrayBuilder *this, const char *src, int length) { - char *str = (char *) calloc(length + 1, sizeof(char)); - memcpy(str, src, length); - - SymArrayItem *item = (SymArrayItem *) calloc(1, sizeof(SymArrayItem)); - if (this->head == NULL) { - this->head = item; - } else { - this->tail->next = item; - item->previous = this->tail; - } - item->str = str; - this->tail = item; - this->size++; -} - -char * SymArrayBuilder_get(SymArrayBuilder *this, int index) { - SymArrayItem *item = this->head; - while (item != NULL && index-- > 0) { - item = item->next; - } - return item->str; -} - -char * SymArrayBuilder_to_string(SymArrayBuilder *this, int index) { - char *value = this->get(this, index); - char *str = (char *) malloc((strlen(value) * sizeof(char)) + 1); - return strcpy(str, value); -} - -char ** SymArrayBuilder_to_array_range(SymArrayBuilder *this, int startIndex, int endIndex) { - char **array = (char **) calloc(endIndex - startIndex, sizeof(char *)); - SymArrayItem *item = this->head; - int i, j; - for (i = 0, j = 0; item != NULL; i++) { - if (i >= startIndex && i < endIndex) { - char *str = (char *) malloc((strlen(item->str) * sizeof(char)) + 1); - strcpy(str, item->str); - array[j++] = str; - } - item = item->next; - } - return array; -} - -char ** SymArrayBuilder_to_array(SymArrayBuilder *this) { - return this->to_array_range(this, 0, this->size); -} - -void SymArrayBuilder_reset(SymArrayBuilder *this) { - SymArrayItem *item = this->head; - while (item != NULL) { - free(item->str); - SymArrayItem *nextItem = item->next; - free(item); - item = nextItem; - } - this->head = this->tail = NULL; - this->size = 0; -} - -void SymArrayBuilder_destroy(SymArrayBuilder *this) { - this->reset(this); - free(this); -} - -void SymArrayBuilder_destroy_array(char **array, int size) { - if (size > 0) { - int i; - for (i = 0; i < size; i++) { - free(array[i]); - } - free(array); - } -} - -void SymArrayBuilder_print_array(char **array, int size) { - if (size > 0) { - int i; - for (i = 0; i < size; i++) { - printf("%s", array[i]); - if (i + 1 < size) { - printf("|"); - } - } - printf("\n"); - } -} - -char ** SymArrayBuilder_copy_array(char **array, int size) { - char **copy = NULL; - if (array != NULL && size > 0) { - int i; - copy = (char **) calloc(size, sizeof(char *)); - for (i = 0; i < size; i++) { - copy[i] = strcpy((char *) calloc(strlen(array[i]) + 1, sizeof(char)), array[i]); - } - } - return copy; -} - -SymArrayBuilder * SymArrayBuilder_new_with_string(char *str) { - SymArrayBuilder *ab = SymArrayBuilder_new(); - ab->add(ab, str); - return ab; -} - -SymArrayBuilder * SymArrayBuilder_new() { - SymArrayBuilder *this = (SymArrayBuilder *) calloc(1, sizeof(SymArrayBuilder)); - this->add = (void *) &SymArrayBuilder_add; - this->addn = (void *) &SymArrayBuilder_addn; - this->get = (void *) &SymArrayBuilder_get; - this->to_string = (void *) &SymArrayBuilder_to_string; - this->to_array = (void *) &SymArrayBuilder_to_array; - this->to_array_range = (void *) &SymArrayBuilder_to_array_range; - this->reset = (void *) &SymArrayBuilder_reset; - this->destroy = (void *) &SymArrayBuilder_destroy; - return this; -} diff --git a/symmetric-client-clib/src/util/List.c b/symmetric-client-clib/src/util/List.c new file mode 100644 index 0000000000..97e11e19ef --- /dev/null +++ b/symmetric-client-clib/src/util/List.c @@ -0,0 +1,119 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "util/List.h" + +void SymList_add(SymList *this, void *object) { + SymListItem *item = (SymListItem *) calloc(1, sizeof(SymListItem)); + if (this->head == NULL) { + this->head = item; + } else { + this->tail->next = item; + item->previous = this->tail; + } + item->object = object; + this->tail = item; + this->size++; +} + +void * SymList_get(SymList *this, int index) { + SymListItem *item = this->head; + while (item != NULL && index-- > 0) { + item = item->next; + } + return item->object; +} + +unsigned short SymIterator_has_next(SymIterator *this) { + return this->index + 1 < this->size && this->currentItem != NULL; +} + +void * SymIterator_next(SymIterator *this) { + this->index++; + void *object = this->currentItem->object; + this->currentItem = this->currentItem->next; + return object; +} + +void SymIterator_destroy(SymIterator *this) { + free(this); +} + +SymIterator * SymList_iterator_from_index(SymList *this, int startIndex) { + SymIterator *iter = (SymIterator *) calloc(1, sizeof(SymIterator)); + iter->size = this->size; + iter->index = -1; + iter->currentItem = this->head; + iter->has_next = (void *) &SymIterator_has_next; + iter->next = (void *) &SymIterator_next; + iter->destroy = (void *) &SymIterator_destroy; + + while (startIndex-- > 0 && iter->has_next(iter)) { + iter->next(iter); + } + return iter; +} + +SymIterator * SymList_iterator(SymList *this) { + return SymList_iterator_from_index(this, 0); +} + +void SymList_reset_all(SymList *this, void *destroy_object(void * object)) { + SymListItem *item = this->head; + while (item != NULL) { + if (destroy_object != NULL) { + destroy_object(item->object); + } + SymListItem *nextItem = item->next; + free(item); + item = nextItem; + } + this->head = this->tail = NULL; + this->size = 0; +} + +void SymList_reset(SymList *this) { + SymList_reset_all(this, NULL); +} + +void SymList_destroy(SymList *this) { + this->reset(this); + free(this); +} + +void SymList_destroy_all(SymList *this, void *destroy_object(void * object)) { + SymList_reset_all(this, destroy_object); + free(this); +} + +SymList * SymList_new(SymList *this) { + if (this == NULL) { + this = (SymList *) calloc(1, sizeof(SymList)); + } + this->add = (void *) &SymList_add; + this->get = (void *) &SymList_get; + this->iterator = (void *) &SymList_iterator; + this->iterator_from_index = (void *) &SymList_iterator_from_index; + this->reset = (void *) &SymList_reset; + this->reset_all = (void *) &SymList_reset_all; + this->destroy = (void *) &SymList_destroy; + this->destroy_all = (void *) &SymList_destroy_all; + return this; +} diff --git a/symmetric-client-clib/src/util/Map.c b/symmetric-client-clib/src/util/Map.c new file mode 100644 index 0000000000..182b372545 --- /dev/null +++ b/symmetric-client-clib/src/util/Map.c @@ -0,0 +1,151 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "util/Map.h" + +static int SymMap_hash(SymMap *this, char *key) { + unsigned long int hash; + unsigned int i; + int len = strlen(key); + for (i = 0, hash = 0; hash < ULONG_MAX && i < len; i++) { + hash = hash << 8; + hash += key[i]; + } + return hash % this->size; +} + +static SymMapEntry * SymMap_new_entry(char *key, void *value, int size) { + SymMapEntry *entry; + + if ((entry = malloc(sizeof(SymMapEntry))) == NULL) { + return NULL; + } + + if ((entry->key = strdup(key)) == NULL) { + return NULL; + } + + if (value != NULL) { + entry->value = memcpy(malloc(size), value, size); + entry->sizeBytes = size; + } else { + entry->value = NULL; + entry->sizeBytes = 0; + } + + entry->next = NULL; + return entry; +} + +void SymMap_put(SymMap *this, char *key, void *value, int size) { + int hash = SymMap_hash(this, key); + + SymMapEntry *next = this->table[hash]; + + SymMapEntry *last = NULL; + while (next != NULL && next->key != NULL && strcmp(key, next->key) > 0) { + last = next; + next = next->next; + } + + if (next != NULL && next->key != NULL && strcmp(key, next->key) == 0) { + free(next->value); + next->value = malloc(size); + if (value != NULL) { + memcpy(next->value, value, size); + next->sizeBytes = size; + } else { + next->value = NULL; + next->sizeBytes = 0; + } + } else { + SymMapEntry *entry = SymMap_new_entry(key, value, size); + + if (next == this->table[hash]) { + entry->next = next; + this->table[hash] = entry; + } else if (next == NULL) { + last->next = entry; + } else { + entry->next = next; + last->next = entry; + } + } +} + +void * SymMap_get(SymMap *this, char *key) { + int hash = SymMap_hash(this, key); + + SymMapEntry *entry = this->table[hash]; + while (entry != NULL && entry->key != NULL && strcmp(key, entry->key) > 0) { + entry = entry->next; + } + + if (entry == NULL || entry->key == NULL || strcmp(key, entry->key) != 0) { + return NULL; + } else { + return entry->value; + } +} + +int SymMap_get_bytes_size(SymMap *this, char *key) { + int hash = SymMap_hash(this, key); + + SymMapEntry *entry = this->table[hash]; + while (entry != NULL && entry->key != NULL && strcmp(key, entry->key) > 0) { + entry = entry->next; + } + + if (entry == NULL || entry->key == NULL || strcmp(key, entry->key) != 0) { + return 0; + } else { + return entry->sizeBytes; + } +} + +void SymMap_destroy(SymMap *this) { + // TODO: free all the malloc'ed memory + free(this); +} + +SymMap * SymMap_new(SymMap *this, int size) { + if (this == NULL) { + this = malloc(sizeof(SymMap)); + } + this->get = (void *) &SymMap_get; + this->get_bytes_size = (void *) &SymMap_get_bytes_size; + this->put = (void *) &SymMap_put; + this->destroy = (void *) &SymMap_destroy; + + if (size < 1) { + size = 1; + } + + if ((this->table = malloc(sizeof(SymMapEntry *) * size)) == NULL) { + return NULL; + } + + int i; + for (i = 0; i < size; i++) { + this->table[i] = NULL; + } + this->size = size; + return this; +} diff --git a/symmetric-client-clib/src/util/Properties.c b/symmetric-client-clib/src/util/Properties.c index ed2d17cfb7..257cd353a6 100644 --- a/symmetric-client-clib/src/util/Properties.c +++ b/symmetric-client-clib/src/util/Properties.c @@ -23,32 +23,28 @@ char * SymProperties_get(SymProperties *this, char *key, char *defaultValue) { int i; for (i = 0; i < this->index; i++) { - if (strcmp(this->list[i]->key, key) == 0) { - return this->list[i]->value; + if (strcmp(this->propArray[i].key, key) == 0) { + return this->propArray[i].value; } } return defaultValue; } void SymProperties_put(SymProperties *this, char *key, char *value) { - SymProperty *property = (SymProperty *) calloc(1, sizeof(SymProperty)); - property->key = key; - property->value = value; - this->list[this->index++] = property; + this->propArray[this->index].key = key; + this->propArray[this->index].value = value; + this->index++; } void SymProperties_put_all(SymProperties *this, SymProperties *properties) { int i; for (i = 0; i < properties->index; i++) { - this->put(this, properties->list[i]->key, properties->list[i]->value); + this->put(this, properties->propArray[i].key, properties->propArray[i].value); } } void SymProperties_destroy(SymProperties *this) { - while (this->index > 0) { - free(this->list[--(this->index)]); - } - free(this->list); + free(this->propArray); free(this); } @@ -56,8 +52,7 @@ SymProperties * SymProperties_new(SymProperties *this) { if (this == NULL) { this = (SymProperties *) calloc(1, sizeof(SymProperties)); } - this->list = (SymProperty **) calloc(1, sizeof(SymProperty *)); - this->list[0] = (SymProperty *) calloc(255, sizeof(SymProperty)); + this->propArray = (SymProperty *) calloc(255, sizeof(SymProperty)); this->get = (void *) &SymProperties_get; this->put = (void *) &SymProperties_put; this->put_all = (void *) &SymProperties_put_all; diff --git a/symmetric-client-clib/src/util/StringArray.c b/symmetric-client-clib/src/util/StringArray.c new file mode 100644 index 0000000000..225f025c4a --- /dev/null +++ b/symmetric-client-clib/src/util/StringArray.c @@ -0,0 +1,113 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "util/StringArray.h" + +void SymStringArray_addn(SymStringArray *this, char *src, int size) { + if (this->size == this->sizeAllocated) { + this->sizeAllocated += this->sizeIncrement; + this->array = realloc(this->array, this->sizeAllocated); + } + char *str = NULL; + if (size > 0) { + str = (char *) memcpy(malloc(size + 1), src, size); + str[size] = '\0'; + } + this->array[this->size++] = str; +} + +void SymStringArray_add(SymStringArray *this, char *src) { + int size = 0; + if (src) { + size = strlen(src); + } + SymStringArray_addn(this, src, size); +} + +void * SymStringArray_get(SymStringArray *this, int index) { + return this->array[index]; +} + +unsigned short SymStringArray_contains(SymStringArray *this, char *findStr) { + int i; + for (i = 0; i < this->size; i++) { + if (strcmp(this->array[i], findStr) == 0) { + return 1; + } + } + return 0; +} + +SymStringArray * SymStringArray_subarray(SymStringArray *this, int startIndex, int endIndex) { + SymStringArray *strArray = SymStringArray_new_with_size(NULL, endIndex - startIndex, this->sizeIncrement); + int i; + for (i = startIndex; i < endIndex; i++) { + strArray->add(strArray, this->array[i]); + } + return strArray; +} + +void SymStringArray_print(SymStringArray *this) { + if (this->size > 0) { + int i; + for (i = 0; i < this->size; i++) { + printf("%s", this->array[i]); + if (i + 1 < this->size) { + printf("|"); + } + } + printf("\n"); + } +} + +void SymStringArray_reset(SymStringArray *this) { + int i; + for (i = 0; i < this->size; i++) { + free(this->array[i]); + } + this->size = 0; +} + +void SymStringArray_destroy(SymStringArray *this) { + this->reset(this); + free(this); +} + +SymStringArray * SymStringArray_new(SymStringArray *this) { + return SymStringArray_new_with_size(this, SYM_STRING_ARRAY_SIZE_INITIAL, SYM_STRING_ARRAY_SIZE_INCREMENT); +} + +SymStringArray * SymStringArray_new_with_size(SymStringArray *this, int sizeInitial, int sizeIncrement) { + if (this == NULL) { + this = (SymStringArray *) calloc(1, sizeof(SymStringArray)); + } + this->sizeAllocated = sizeInitial; + this->sizeIncrement = sizeIncrement; + this->array = (char **) malloc(this->sizeAllocated * sizeof(char *)); + this->add = (void *) &SymStringArray_add; + this->addn = (void *) &SymStringArray_addn; + this->get = (void *) &SymStringArray_get; + this->contains = (void *) &SymStringArray_contains; + this->subarray = (void *) &SymStringArray_subarray; + this->print = (void *) &SymStringArray_print; + this->reset = (void *) &SymStringArray_reset; + this->destroy = (void *) &SymStringArray_destroy; + return this; +} diff --git a/symmetric-client-clib/src/util/StringBuilder.c b/symmetric-client-clib/src/util/StringBuilder.c index 03ec415c85..fc5e70374d 100644 --- a/symmetric-client-clib/src/util/StringBuilder.c +++ b/symmetric-client-clib/src/util/StringBuilder.c @@ -20,7 +20,7 @@ */ #include "util/StringBuilder.h" -void SymStringBuilder_appendn(SymStringBuilder *this, const char *src, int length) { +SymStringBuilder * SymStringBuilder_appendn(SymStringBuilder *this, const char *src, int length) { int sdiff = length - (this->size - this->pos) + 1; if (sdiff > 0) { this->size = this->size + sdiff; @@ -28,30 +28,35 @@ void SymStringBuilder_appendn(SymStringBuilder *this, const char *src, int lengt } memcpy(this->str + this->pos, src, length); this->pos += length; - this->str[this->pos] = NULL; + this->str[this->pos] = '\0'; + return this; } -void SymStringBuilder_append(SymStringBuilder *this, const char *src) { - if (src != NULL) { - SymStringBuilder_appendn(this, src, strlen(src)); +SymStringBuilder * SymStringBuilder_append(SymStringBuilder *this, const char *src) { + if (src == NULL) { + src = "(null)"; } + return SymStringBuilder_appendn(this, src, strlen(src)); } -void SymStringBuilder_appendf(SymStringBuilder *this, const char *fmt, ...) { - char *str; +SymStringBuilder * SymStringBuilder_appendf(SymStringBuilder *this, const char *fmt, ...) { va_list arglist; + va_start(arglist, fmt); + int sizeNeeded = vsnprintf(NULL, 0, fmt, arglist) + 1; + va_end(arglist); + char *str = malloc(sizeNeeded + 1); va_start(arglist, fmt); - vsprintf(&str, fmt, arglist); + vsprintf(str, fmt, arglist); va_end(arglist); - if (str) { - SymStringBuilder_append(this, str); - free(str); - } + SymStringBuilder_appendn(this, str, sizeNeeded); + free(str); + return this; } char * SymStringBuilder_to_string(SymStringBuilder *this) { + // TODO: this should return a copy return this->str; } @@ -80,6 +85,11 @@ char * SymStringBuilder_copy(char *str) { return NULL; } +void SymStringBuilder_copy_to_field(char **strField, char *str) { + free(*strField); + *strField = SymStringBuilder_copy(str); +} + SymStringBuilder * SymStringBuilder_new() { return SymStringBuilder_new_with_size(SYM_STRING_BUILDER_SIZE); }