diff --git a/uppsrc/Sql/MassInsert.cpp b/uppsrc/Sql/MassInsert.cpp new file mode 100644 index 000000000..4430f3e62 --- /dev/null +++ b/uppsrc/Sql/MassInsert.cpp @@ -0,0 +1,87 @@ +#include "Sql.h" + +namespace Upp { + +SqlMassInsert::~SqlMassInsert() +{ + Flush(); +} + +SqlMassInsert& SqlMassInsert::operator()(SqlId col, const Value& val) +{ + if(pos == 0) { + cache.Add().nulls = 0; + cache.Top(); + } + if(cache.GetCount() == 1) + column.Add(~col); + else + ASSERT(column[pos] == ~col); + Row& r = cache.Top(); + r.value.Add(val); + if(IsNull(val)) + r.nulls |= (1 << pos); + pos++; + ASSERT(pos < 30); + return *this; +} + +SqlMassInsert& SqlMassInsert::EndRow() +{ + if(cache.GetCount() && cache[0].value.GetCount() * cache.GetCount() > 5000) + Flush(); + ASSERT(column.GetCount() == pos); + pos = 0; + return *this; +} + +void SqlMassInsert::Flush() +{ + const dword DONE = 0xffffffff; + if(cache.GetCount() == 0) + return; + for(int ii = 0; ii < cache.GetCount(); ii++) { + dword nulls = cache[ii].nulls; + if(nulls != DONE) { + String insert; + insert << "insert into " + table + '('; + bool nextcol = false; + for(int i = 0; i < column.GetCount(); i++) { + if(!(nulls & (1 << i))) { + if(nextcol) + insert << ", "; + nextcol = true; + insert << column[i]; + } + } + insert << ')'; + bool nextsel = false; + for(int i = ii; i < cache.GetCount(); i++) { + Row& r = cache[i]; + if(r.nulls == nulls) { + r.nulls = DONE; + if(nextsel) + insert << " union all"; + nextsel = true; + insert << " select "; + bool nextval = false; + for(int i = 0; i < r.value.GetCount(); i++) + if(!(nulls & (1 << i))) { + if(nextval) + insert << ", "; + nextval = true; + insert << SqlCompile(sql.GetDialect(), SqlFormat(r.value[i])); + } + if(sql.GetDialect() == ORACLE) + insert << " from dual"; + } + } + sql.Execute(insert); + } + } + cache.Clear(); + column.Clear(); + pos = 0; +} + +}; diff --git a/uppsrc/Sql/Sql.upp b/uppsrc/Sql/Sql.upp index ee54d92c2..af8a35fac 100644 --- a/uppsrc/Sql/Sql.upp +++ b/uppsrc/Sql/Sql.upp @@ -18,6 +18,7 @@ file SqlStatement.cpp, Sqls.h, Sql.cpp, + MassInsert.cpp, Schema readonly separator, SqlSchema.h, SqlSchema.cpp, diff --git a/uppsrc/Sql/SqlCase.cpp b/uppsrc/Sql/SqlCase.cpp index 78277b138..7f2ed20cb 100644 --- a/uppsrc/Sql/SqlCase.cpp +++ b/uppsrc/Sql/SqlCase.cpp @@ -94,6 +94,8 @@ void SqlCompile(const char *&s, StringBuffer *r, byte dialect) case ORACLE: *r << Format("to_date('%d/%d/%d', 'SYYYY/MM/DD')", x.year, x.month, x.day); break; + case PGSQL: + *r << "date "; default: *r << Format("\'%04d-%02d-%02d\'", x.year, x.month, x.day); } @@ -119,6 +121,8 @@ void SqlCompile(const char *&s, StringBuffer *r, byte dialect) *r << Format("to_date('%d/%d/%d/%d', 'SYYYY/MM/DD/SSSSS')", x.year, x.month, x.day, x.second + 60 * (x.minute + 60 * x.hour)); break; + case PGSQL: + *r << "timestamp "; default: *r << Format("\'%04d-%02d-%02d %02d:%02d:%02d\'", x.year, x.month, x.day, x.hour, x.minute, x.second); diff --git a/uppsrc/Sql/Sqls.h b/uppsrc/Sql/Sqls.h index e178b3c36..29c38e8e3 100644 --- a/uppsrc/Sql/Sqls.h +++ b/uppsrc/Sql/Sqls.h @@ -389,3 +389,30 @@ StatementExecutor& SQLStatementExecutor(); typedef Sql QSql; typedef SqlSession QSession; #endif + +class SqlMassInsert { + struct Row : Moveable { + dword nulls; + Vector value; + }; + + Sql& sql; + String table; + Vector column; + Vector cache; + int pos; + + void NewRow(); + + +public: + SqlMassInsert& operator()(SqlId col, const Value& val); + SqlMassInsert& EndRow(); + void Flush(); + + SqlMassInsert(Sql& sql, SqlId table) : sql(sql), table(~table) { pos = 0; } +#ifndef NOAPPSQL + SqlMassInsert(SqlId table) : sql(SQL), table(~table) { pos = 0; } +#endif + ~SqlMassInsert(); +};