mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Merge branch 'release/0.0.0'
This commit is contained in:
77
.circleci/config.yml
Normal file
77
.circleci/config.yml
Normal file
@ -0,0 +1,77 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
java17:
|
||||
docker:
|
||||
- image: 'cimg/openjdk:17.0'
|
||||
resource_class: small
|
||||
|
||||
orbs:
|
||||
slack: circleci/slack@4.10.1
|
||||
|
||||
commands:
|
||||
run_maven:
|
||||
parameters:
|
||||
maven_subcommand:
|
||||
default: test
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "pom.xml" }}
|
||||
- run:
|
||||
name: Run Maven
|
||||
command: |
|
||||
mvn -s .circleci/mvn-settings.xml << parameters.maven_subcommand >>
|
||||
- run:
|
||||
name: Save test results
|
||||
command: |
|
||||
mkdir -p ~/test-results/junit/
|
||||
find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/test-results/junit/ \;
|
||||
when: always
|
||||
- store_test_results:
|
||||
path: ~/test-results
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: v1-dependencies-{{ checksum "pom.xml" }}
|
||||
|
||||
jobs:
|
||||
mvn_test:
|
||||
executor: java17
|
||||
steps:
|
||||
- run_maven:
|
||||
maven_subcommand: test
|
||||
- slack/notify:
|
||||
event: fail
|
||||
|
||||
mvn_deploy:
|
||||
executor: java17
|
||||
steps:
|
||||
- run_maven:
|
||||
maven_subcommand: deploy
|
||||
- slack/notify:
|
||||
event: always
|
||||
|
||||
workflows:
|
||||
test_only:
|
||||
jobs:
|
||||
- mvn_test:
|
||||
context: [ qqq-maven-registry-credentials, kingsrook-slack ]
|
||||
filters:
|
||||
branches:
|
||||
ignore: /dev/
|
||||
tags:
|
||||
ignore: /(version|snapshot)-.*/
|
||||
|
||||
deploy:
|
||||
jobs:
|
||||
- mvn_deploy:
|
||||
context: [ qqq-maven-registry-credentials, kingsrook-slack ]
|
||||
filters:
|
||||
branches:
|
||||
only: /dev/
|
||||
tags:
|
||||
only: /(version|snapshot)-.*/
|
||||
|
9
.circleci/mvn-settings.xml
Normal file
9
.circleci/mvn-settings.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>github-qqq-maven-registry</id>
|
||||
<username>${env.QQQ_MAVEN_REGISTRY_USERNAME}</username>
|
||||
<password>${env.QQQ_MAVEN_REGISTRY_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
37
.github/workflows/maven.yml
vendored
37
.github/workflows/maven.yml
vendored
@ -1,37 +0,0 @@
|
||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Java CI with Maven
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: maven-settings-xml-action
|
||||
uses: whelk-io/maven-settings-xml-action@v20
|
||||
with:
|
||||
repositories: '[{ "id": "github-qqq-maven-registry", "url": "https://maven.pkg.github.com/Kingsrook/qqq-maven-registry", "snapshots": { "enabled": "true" }}]'
|
||||
servers: '[{ "id": "github-qqq-maven-registry", "username": "${{ secrets.QQQ_MAVEN_REGISTRY_USERNAME }}", "password": "${{ secrets.QQQ_MAVEN_REGISTRY_PASSWORD }}" }]'
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
- name: Publish to GitHub Packages Apache Maven
|
||||
run: mvn deploy
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
@ -5,8 +5,7 @@ This is a backend-module for the qqq framework - specifically, one that works wi
|
||||
QQQ - Low-code Application Framework for Engineers. \
|
||||
Copyright (C) 2022. Kingsrook, LLC \
|
||||
651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States \
|
||||
contact@kingsrook.com
|
||||
https://github.com/Kingsrook/intellij-commentator-plugin
|
||||
contact@kingsrook.com | https://github.com/Kingsrook/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
|
@ -46,6 +46,7 @@
|
||||
-->
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
@ -171,7 +172,7 @@
|
||||
<property name="caseIndent" value="3"/>
|
||||
<property name="throwsIndent" value="6"/>
|
||||
<property name="lineWrappingIndentation" value="3"/>
|
||||
<property name="arrayInitIndent" value="2"/>
|
||||
<property name="arrayInitIndent" value="6"/>
|
||||
</module>
|
||||
<!--
|
||||
<module name="AbbreviationAsWordInName">
|
||||
@ -260,4 +261,5 @@
|
||||
<module name="MissingOverride"/>
|
||||
|
||||
</module>
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
</module>
|
||||
|
45
pom.xml
45
pom.xml
@ -20,14 +20,18 @@
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-module-rdbms</artifactId>
|
||||
<version>0.0-SNAPSHOT</version>
|
||||
<version>0.0.0</version>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<!-- props specifically to this module -->
|
||||
@ -47,7 +51,7 @@
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-core</artifactId>
|
||||
<version>0.0-SNAPSHOT</version>
|
||||
<version>0.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 3rd party deps specifically for this module -->
|
||||
@ -59,7 +63,7 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.197</version>
|
||||
<version>2.1.210</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@ -72,12 +76,12 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.15.0</version>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.15.0</version>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
@ -85,6 +89,12 @@
|
||||
<version>5.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.23.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -138,12 +148,29 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.amashchenko.maven.plugin</groupId>
|
||||
<artifactId>gitflow-maven-plugin</artifactId>
|
||||
<version>1.18.0</version>
|
||||
<configuration>
|
||||
<gitFlowConfig>
|
||||
<productionBranch>main</productionBranch>
|
||||
<developmentBranch>dev</developmentBranch>
|
||||
<versionTagPrefix>version-</versionTagPrefix>
|
||||
</gitFlowConfig>
|
||||
<skipFeatureVersion>true</skipFeatureVersion> <!-- Keep feature names out of versions -->
|
||||
<postReleaseGoals>install</postReleaseGoals> <!-- Let CI run deploys -->
|
||||
<commitDevelopmentVersionAtStart>true</commitDevelopmentVersionAtStart>
|
||||
<versionDigitToIncrement>1</versionDigitToIncrement> <!-- In general, we update the minor -->
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>github</id>
|
||||
<id>github-qqq-maven-registry</id>
|
||||
<name>GitHub QQQ Maven Registry</name>
|
||||
<url>https://maven.pkg.github.com/Kingsrook/qqq-maven-registry</url>
|
||||
</repository>
|
||||
|
@ -22,6 +22,8 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
|
||||
@ -31,21 +33,54 @@ import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSDeleteAction;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSInsertAction;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSQueryAction;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSUpdateAction;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** RDBMSBackendModule
|
||||
*
|
||||
** QQQ Backend module for working with Relational Databases (RDBMS's).
|
||||
*******************************************************************************/
|
||||
public class RDBMSBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Method where a backend module must be able to provide its type (name).
|
||||
*******************************************************************************/
|
||||
public String getBackendType()
|
||||
{
|
||||
return ("rdbms");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QBackendMetaData> getBackendMetaDataClass()
|
||||
{
|
||||
return (RDBMSBackendMetaData.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for table-backend details for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QTableBackendDetails> getTableBackendDetailsClass()
|
||||
{
|
||||
return (RDBMSTableBackendDetails.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QueryInterface getQueryInterface()
|
||||
{
|
||||
return new RDBMSQueryAction();
|
||||
return (new RDBMSQueryAction());
|
||||
}
|
||||
|
||||
|
||||
@ -60,6 +95,7 @@ public class RDBMSBackendModule implements QBackendModuleInterface
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -79,4 +115,5 @@ public class RDBMSBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
return (new RDBMSDeleteAction());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,17 +22,49 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.time.OffsetDateTime;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractQTableRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Base class for all core actions in the RDBMS module.
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractRDBMSAction
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the table name to use in the RDBMS from a QTableMetaData.
|
||||
**
|
||||
** That is, table.backendDetails.tableName if set -- else, table.name
|
||||
*******************************************************************************/
|
||||
protected String getTableName(QTableMetaData table)
|
||||
{
|
||||
if(table.getBackendDetails() instanceof RDBMSTableBackendDetails details)
|
||||
{
|
||||
if(StringUtils.hasContent(details.getTableName()))
|
||||
{
|
||||
return (details.getTableName());
|
||||
}
|
||||
}
|
||||
return (table.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the column name to use for a field in the RDBMS, from the fieldMetaData.
|
||||
**
|
||||
** That is, field.backendName if set -- else, field.name
|
||||
*******************************************************************************/
|
||||
protected String getColumnName(QFieldMetaData field)
|
||||
{
|
||||
@ -43,4 +75,42 @@ public abstract class AbstractRDBMSAction
|
||||
return (field.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get a database connection, per the backend in the request.
|
||||
*******************************************************************************/
|
||||
protected Connection getConnection(AbstractQTableRequest qTableRequest) throws SQLException
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
return connectionManager.getConnection((RDBMSBackendMetaData) qTableRequest.getBackend());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Handle obvious problems with values - like empty string for integer should be null.
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected Serializable scrubValue(QFieldMetaData field, Serializable value)
|
||||
{
|
||||
if("".equals(value))
|
||||
{
|
||||
QFieldType type = field.getType();
|
||||
if(type.equals(QFieldType.INTEGER) || type.equals(QFieldType.DECIMAL) || type.equals(QFieldType.DATE) || type.equals(QFieldType.DATE_TIME))
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// todo - let this come from something in the field //
|
||||
//////////////////////////////////////////////////////
|
||||
if(value == null && (field.getName().equals("createDate") || field.getName().equals("modifyDate")))
|
||||
{
|
||||
value = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,8 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
|
||||
|
||||
@ -55,7 +52,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
|
||||
DeleteResult rs = new DeleteResult();
|
||||
QTableMetaData table = deleteRequest.getTable();
|
||||
|
||||
String tableName = table.getName();
|
||||
String tableName = getTableName(table);
|
||||
String primaryKeyName = getColumnName(table.getField(table.getPrimaryKeyField()));
|
||||
String sql = "DELETE FROM "
|
||||
+ tableName
|
||||
@ -68,18 +65,16 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
|
||||
|
||||
// todo sql customization - can edit sql and/or param list
|
||||
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(deleteRequest.getBackend()));
|
||||
|
||||
Connection connection = getConnection(deleteRequest);
|
||||
QueryManager.executeUpdateForRowCount(connection, sql, params);
|
||||
List<QRecordWithStatus> recordsWithStatus = new ArrayList<>();
|
||||
rs.setRecords(recordsWithStatus);
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
rs.setRecords(outputRecords);
|
||||
for(Serializable primaryKey : deleteRequest.getPrimaryKeys())
|
||||
{
|
||||
QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withValue("id", primaryKey);
|
||||
// todo uh, identify any errors?
|
||||
QRecordWithStatus recordWithStatus = new QRecordWithStatus(qRecord);
|
||||
recordsWithStatus.add(recordWithStatus);
|
||||
QRecord outputRecord = new QRecord(qRecord);
|
||||
outputRecords.add(outputRecord);
|
||||
}
|
||||
|
||||
return rs;
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -30,12 +31,10 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
|
||||
|
||||
@ -50,9 +49,14 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
||||
*******************************************************************************/
|
||||
public InsertResult execute(InsertRequest insertRequest) throws QException
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(insertRequest.getRecords()))
|
||||
{
|
||||
throw (new QException("Request to insert 0 records."));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
InsertResult rs = new InsertResult();
|
||||
InsertResult rs = new InsertResult();
|
||||
QTableMetaData table = insertRequest.getTable();
|
||||
|
||||
List<QFieldMetaData> insertableFields = table.getFields().values().stream()
|
||||
@ -66,9 +70,9 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
||||
.map(x -> "?")
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
String tableName = table.getName();
|
||||
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
|
||||
List<Object> params = new ArrayList<>();
|
||||
String tableName = getTableName(table);
|
||||
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
int recordIndex = 0;
|
||||
for(QRecord record : insertRequest.getRecords())
|
||||
@ -80,31 +84,32 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
||||
sql.append("(").append(questionMarks).append(")");
|
||||
for(QFieldMetaData field : insertableFields)
|
||||
{
|
||||
params.add(record.getValue(field.getName()));
|
||||
Serializable value = record.getValue(field.getName());
|
||||
value = scrubValue(field, value);
|
||||
|
||||
params.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
// todo sql customization - can edit sql and/or param list
|
||||
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(insertRequest.getBackend()));
|
||||
|
||||
// QueryResult rs = new QueryResult();
|
||||
// List<QRecord> records = new ArrayList<>();
|
||||
// rs.setRecords(records);
|
||||
|
||||
// todo - non-serial-id style tables
|
||||
// todo - other generated values, e.g., createDate... maybe need to re-select?
|
||||
List<Integer> idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params);
|
||||
List<QRecordWithStatus> recordsWithStatus = new ArrayList<>();
|
||||
rs.setRecords(recordsWithStatus);
|
||||
Connection connection = getConnection(insertRequest);
|
||||
List<Integer> idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params);
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
rs.setRecords(outputRecords);
|
||||
int index = 0;
|
||||
for(QRecord record : insertRequest.getRecords())
|
||||
{
|
||||
Integer id = idList.get(index++);
|
||||
QRecordWithStatus recordWithStatus = new QRecordWithStatus(record);
|
||||
recordWithStatus.setValue(table.getPrimaryKeyField(), id);
|
||||
recordsWithStatus.add(recordWithStatus);
|
||||
Integer id = idList.get(index++);
|
||||
QRecord outputRecord = new QRecord(record);
|
||||
outputRecord.setValue(table.getPrimaryKeyField(), id);
|
||||
outputRecords.add(outputRecord);
|
||||
}
|
||||
|
||||
return rs;
|
||||
|
@ -44,9 +44,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -54,6 +54,7 @@ import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
*******************************************************************************/
|
||||
public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterface
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(RDBMSQueryAction.class);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -63,7 +64,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
try
|
||||
{
|
||||
QTableMetaData table = queryRequest.getTable();
|
||||
String tableName = table.getName();
|
||||
String tableName = getTableName(table);
|
||||
|
||||
List<QFieldMetaData> fieldList = new ArrayList<>(table.getFields().values());
|
||||
String columns = fieldList.stream()
|
||||
@ -97,13 +98,11 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
|
||||
// todo sql customization - can edit sql and/or param list
|
||||
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(queryRequest.getBackend()));
|
||||
|
||||
QueryResult rs = new QueryResult();
|
||||
List<QRecord> records = new ArrayList<>();
|
||||
rs.setRecords(records);
|
||||
|
||||
Connection connection = getConnection(queryRequest);
|
||||
QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) ->
|
||||
{
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
@ -131,7 +130,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOG.warn("Error executing query", e);
|
||||
throw new QException("Error executing query", e);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -30,12 +31,9 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.update.UpdateRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.update.UpdateResult;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.interfaces.UpdateInterface;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
|
||||
|
||||
@ -52,15 +50,16 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateResult rs = new UpdateResult();
|
||||
UpdateResult rs = new UpdateResult();
|
||||
QTableMetaData table = updateRequest.getTable();
|
||||
|
||||
List<QRecordWithStatus> recordsWithStatus = new ArrayList<>();
|
||||
rs.setRecords(recordsWithStatus);
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
rs.setRecords(outputRecords);
|
||||
|
||||
// todo - sql batch for performance
|
||||
// todo - if setting a bunch of records to have the same value, a single update where id IN?
|
||||
int recordIndex = 0;
|
||||
Connection connection = getConnection(updateRequest);
|
||||
int recordIndex = 0;
|
||||
for(QRecord record : updateRequest.getRecords())
|
||||
{
|
||||
List<QFieldMetaData> updateableFields = table.getFields().values().stream()
|
||||
@ -72,33 +71,34 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
|
||||
.map(f -> this.getColumnName(f) + " = ?")
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
String tableName = table.getName();
|
||||
String tableName = getTableName(table);
|
||||
StringBuilder sql = new StringBuilder("UPDATE ").append(tableName)
|
||||
.append(" SET ").append(columns)
|
||||
.append(" WHERE ").append(getColumnName(table.getField(table.getPrimaryKeyField()))).append(" = ?");
|
||||
|
||||
// todo sql customization - can edit sql and/or param list
|
||||
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(updateRequest.getBackend()));
|
||||
|
||||
QRecordWithStatus recordWithStatus = new QRecordWithStatus(record);
|
||||
recordsWithStatus.add(recordWithStatus);
|
||||
QRecord outputRecord = new QRecord(record);
|
||||
outputRecords.add(outputRecord);
|
||||
|
||||
try
|
||||
{
|
||||
List<Object> params = new ArrayList<>();
|
||||
for(QFieldMetaData field : updateableFields)
|
||||
{
|
||||
params.add(record.getValue(field.getName()));
|
||||
Serializable value = record.getValue(field.getName());
|
||||
value = scrubValue(field, value);
|
||||
params.add(value);
|
||||
}
|
||||
params.add(record.getValue(table.getPrimaryKeyField()));
|
||||
|
||||
QueryManager.executeUpdate(connection, sql.toString(), params);
|
||||
// todo - auto-updated values, e.g., modifyDate... maybe need to re-select?
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
recordWithStatus.setErrors(new ArrayList<>(List.of(e)));
|
||||
// todo - how to communicate errors??? outputRecord.setErrors(new ArrayList<>(List.of(e)));
|
||||
throw new QException("Error executing update: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ package com.kingsrook.qqq.backend.module.rdbms.jdbc;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -652,6 +652,12 @@ public class QueryManager
|
||||
statement.setTimestamp(index, timestamp);
|
||||
return (1);
|
||||
}
|
||||
else if(value instanceof OffsetDateTime)
|
||||
{
|
||||
Timestamp timestamp = new Timestamp(((OffsetDateTime) value).toEpochSecond() * MS_PER_SEC);
|
||||
statement.setTimestamp(index, timestamp);
|
||||
return (1);
|
||||
}
|
||||
else if(value instanceof LocalDateTime)
|
||||
{
|
||||
Timestamp timestamp = new Timestamp(((LocalDateTime) value).toEpochSecond(ZoneOffset.UTC) * MS_PER_SEC);
|
||||
|
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.rdbms.model.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Meta-data to provide details of an RDBMS backend (e.g., connection params)
|
||||
*******************************************************************************/
|
||||
public class RDBMSBackendMetaData extends QBackendMetaData
|
||||
{
|
||||
private String vendor;
|
||||
private String hostName;
|
||||
private Integer port;
|
||||
private String databaseName;
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default Constructor.
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData()
|
||||
{
|
||||
super();
|
||||
setBackendType(RDBMSBackendModule.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for vendor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getVendor()
|
||||
{
|
||||
return vendor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for vendor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setVendor(String vendor)
|
||||
{
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for vendor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData withVendor(String vendor)
|
||||
{
|
||||
this.vendor = vendor;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hostName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getHostName()
|
||||
{
|
||||
return hostName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hostName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setHostName(String hostName)
|
||||
{
|
||||
this.hostName = hostName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for hostName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData withHostName(String hostName)
|
||||
{
|
||||
this.hostName = hostName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for port
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for port
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPort(Integer port)
|
||||
{
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for port
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData withPort(Integer port)
|
||||
{
|
||||
this.port = port;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for databaseName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getDatabaseName()
|
||||
{
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for databaseName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDatabaseName(String databaseName)
|
||||
{
|
||||
this.databaseName = databaseName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for databaseName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData withDatabaseName(String databaseName)
|
||||
{
|
||||
this.databaseName = databaseName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for username
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getUsername()
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for username
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setUsername(String username)
|
||||
{
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for username
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData withUsername(String username)
|
||||
{
|
||||
this.username = username;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for password
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for password
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for password
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData withPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Called by the QInstanceEnricher - to do backend-type-specific enrichments.
|
||||
** Original use case is: reading secrets into fields (e.g., passwords).
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void enrich()
|
||||
{
|
||||
super.enrich();
|
||||
QMetaDataVariableInterpreter interpreter = new QMetaDataVariableInterpreter();
|
||||
username = interpreter.interpret(username);
|
||||
password = interpreter.interpret(password);
|
||||
}
|
||||
|
||||
}
|
@ -19,87 +19,63 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.rdbms;
|
||||
package com.kingsrook.qqq.backend.module.rdbms.model.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** RDBMSBackendMetaData
|
||||
*
|
||||
** Extension of QTableBackendDetails, with details specific to an RDBMS table.
|
||||
*******************************************************************************/
|
||||
public class RDBMSBackendMetaData extends QBackendMetaData
|
||||
public class RDBMSTableBackendDetails extends QTableBackendDetails
|
||||
{
|
||||
private String tableName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Default Constructor.
|
||||
*******************************************************************************/
|
||||
public RDBMSBackendMetaData(QBackendMetaData source)
|
||||
public RDBMSTableBackendDetails()
|
||||
{
|
||||
super();
|
||||
setName(source.getName());
|
||||
setValues(source.getValues());
|
||||
setBackendType(RDBMSBackendModule.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getVendor()
|
||||
public String getTableName()
|
||||
{
|
||||
return getValue("vendor");
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getHostName()
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
return getValue("hostName");
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPort()
|
||||
public RDBMSTableBackendDetails withTableName(String tableName)
|
||||
{
|
||||
return getValue("port");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getDatabaseName()
|
||||
{
|
||||
return getValue("databaseName");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getUsername()
|
||||
{
|
||||
return getValue("username");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPassword()
|
||||
{
|
||||
return getValue("password");
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.rdbms;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QInstance defineInstance()
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.addBackend(defineBackend());
|
||||
qInstance.addTable(defineTablePerson());
|
||||
return (qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static RDBMSBackendMetaData defineBackend()
|
||||
{
|
||||
RDBMSBackendMetaData rdbmsBackendMetaData = new RDBMSBackendMetaData()
|
||||
.withVendor("h2")
|
||||
.withHostName("mem")
|
||||
.withDatabaseName("test_database")
|
||||
.withUsername("sa")
|
||||
.withPassword("");
|
||||
rdbmsBackendMetaData.setName("default");
|
||||
return (rdbmsBackendMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QTableMetaData defineTablePerson()
|
||||
{
|
||||
return new QTableMetaData()
|
||||
.withName("a-person") // use this name, so it isn't the same as the actual database-table name (which must come from the backend details)
|
||||
.withLabel("Person")
|
||||
.withBackendName(defineBackend().getName())
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date"))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("modify_date"))
|
||||
.withField(new QFieldMetaData("firstName", QFieldType.STRING).withBackendName("first_name"))
|
||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING).withBackendName("last_name"))
|
||||
.withField(new QFieldMetaData("birthDate", QFieldType.DATE).withBackendName("birth_date"))
|
||||
.withField(new QFieldMetaData("email", QFieldType.STRING))
|
||||
.withBackendDetails(new RDBMSTableBackendDetails()
|
||||
.withTableName("person"));
|
||||
}
|
||||
|
||||
}
|
@ -25,12 +25,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@ -43,56 +38,6 @@ import static junit.framework.Assert.assertNotNull;
|
||||
public class RDBMSActionTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected QInstance defineInstance()
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.addBackend(defineBackend());
|
||||
qInstance.addTable(defineTablePerson());
|
||||
return (qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected QBackendMetaData defineBackend()
|
||||
{
|
||||
return new QBackendMetaData()
|
||||
.withName("default")
|
||||
.withType("rdbms")
|
||||
.withValue("vendor", "h2")
|
||||
.withValue("hostName", "mem")
|
||||
.withValue("databaseName", "test_database")
|
||||
.withValue("username", "sa")
|
||||
.withValue("password", "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData defineTablePerson()
|
||||
{
|
||||
return new QTableMetaData()
|
||||
.withName("person")
|
||||
.withLabel("Person")
|
||||
.withBackendName(defineBackend().getName())
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date"))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("modify_date"))
|
||||
.withField(new QFieldMetaData("firstName", QFieldType.STRING).withBackendName("first_name"))
|
||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING).withBackendName("last_name"))
|
||||
.withField(new QFieldMetaData("birthDate", QFieldType.DATE).withBackendName("birth_date"))
|
||||
.withField(new QFieldMetaData("email", QFieldType.STRING));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -100,11 +45,12 @@ public class RDBMSActionTest
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void primeTestDatabase() throws Exception
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(defineBackend()));
|
||||
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/prime-test-database.sql");
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
||||
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/prime-test-database.sql");
|
||||
assertNotNull(primeTestDatabaseSqlStream);
|
||||
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
||||
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
||||
String joinedSQL = String.join("\n", lines);
|
||||
for(String sql : joinedSQL.split(";"))
|
||||
{
|
||||
@ -120,7 +66,7 @@ public class RDBMSActionTest
|
||||
protected void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(defineBackend()));
|
||||
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
||||
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@ -60,7 +60,7 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
|
||||
deleteRequest.setPrimaryKeys(List.of(1, 2, 3, 4, 5));
|
||||
DeleteResult deleteResult = new RDBMSDeleteAction().execute(deleteRequest);
|
||||
assertEquals(5, deleteResult.getRecords().size(), "Unfiltered delete should return all rows");
|
||||
assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT id FROM person", (rs -> assertFalse(rs.next())));
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
|
||||
deleteRequest.setPrimaryKeys(List.of(1));
|
||||
DeleteResult deleteResult = new RDBMSDeleteAction().execute(deleteRequest);
|
||||
assertEquals(1, deleteResult.getRecords().size(), "Should delete one row");
|
||||
assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT id FROM person WHERE id = 1", (rs -> assertFalse(rs.next())));
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
|
||||
deleteRequest.setPrimaryKeys(List.of(1, 3, 5));
|
||||
DeleteResult deleteResult = new RDBMSDeleteAction().execute(deleteRequest);
|
||||
assertEquals(3, deleteResult.getRecords().size(), "Should delete one row");
|
||||
assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT id FROM person", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
@ -113,8 +113,8 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
|
||||
private DeleteRequest initDeleteRequest()
|
||||
{
|
||||
DeleteRequest deleteRequest = new DeleteRequest();
|
||||
deleteRequest.setInstance(defineInstance());
|
||||
deleteRequest.setTableName(defineTablePerson().getName());
|
||||
deleteRequest.setInstance(TestUtils.defineInstance());
|
||||
deleteRequest.setTableName(TestUtils.defineTablePerson().getName());
|
||||
return deleteRequest;
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,11 @@ import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -67,7 +66,7 @@ public class RDBMSInsertActionTest extends RDBMSActionTest
|
||||
InsertResult insertResult = new RDBMSInsertAction().execute(insertRequest);
|
||||
assertEquals(1, insertResult.getRecords().size(), "Should return 1 row");
|
||||
assertNotNull(insertResult.getRecords().get(0).getValue("id"), "Should have an id in the row");
|
||||
assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
@ -75,6 +74,7 @@ public class RDBMSInsertActionTest extends RDBMSActionTest
|
||||
rowsFound++;
|
||||
assertEquals(6, rs.getInt("id"));
|
||||
assertEquals("James", rs.getString("first_name"));
|
||||
assertNotNull(rs.getString("create_date"));
|
||||
}
|
||||
assertEquals(1, rowsFound);
|
||||
}));
|
||||
@ -104,7 +104,7 @@ public class RDBMSInsertActionTest extends RDBMSActionTest
|
||||
assertEquals(2, insertResult.getRecords().size(), "Should return 1 row");
|
||||
assertEquals(6, insertResult.getRecords().get(0).getValue("id"), "Should have next id in the row");
|
||||
assertEquals(7, insertResult.getRecords().get(1).getValue("id"), "Should have next id in the row");
|
||||
assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'Picard'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
@ -135,8 +135,8 @@ public class RDBMSInsertActionTest extends RDBMSActionTest
|
||||
private InsertRequest initInsertRequest()
|
||||
{
|
||||
InsertRequest insertRequest = new InsertRequest();
|
||||
insertRequest.setInstance(defineInstance());
|
||||
insertRequest.setTableName(defineTablePerson().getName());
|
||||
insertRequest.setInstance(TestUtils.defineInstance());
|
||||
insertRequest.setTableName(TestUtils.defineTablePerson().getName());
|
||||
return insertRequest;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import com.kingsrook.qqq.backend.core.model.actions.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -413,8 +414,8 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
private QueryRequest initQueryRequest()
|
||||
{
|
||||
QueryRequest queryRequest = new QueryRequest();
|
||||
queryRequest.setInstance(defineInstance());
|
||||
queryRequest.setTableName(defineTablePerson().getName());
|
||||
queryRequest.setInstance(TestUtils.defineInstance());
|
||||
queryRequest.setTableName(TestUtils.defineTablePerson().getName());
|
||||
return queryRequest;
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,11 @@ import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.update.UpdateRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.update.UpdateResult;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
@ -69,7 +68,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest);
|
||||
assertEquals(1, updateResult.getRecords().size(), "Should return 1 row");
|
||||
assertEquals(2, updateResult.getRecords().get(0).getValue("id"), "Should have id=2 in the row");
|
||||
assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
@ -114,7 +113,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
assertEquals(2, updateResult.getRecords().size(), "Should return 2 rows");
|
||||
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
|
||||
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
|
||||
assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
@ -148,8 +147,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
private UpdateRequest initUpdateRequest()
|
||||
{
|
||||
UpdateRequest updateRequest = new UpdateRequest();
|
||||
updateRequest.setInstance(defineInstance());
|
||||
updateRequest.setTableName(defineTablePerson().getName());
|
||||
updateRequest.setInstance(TestUtils.defineInstance());
|
||||
updateRequest.setTableName(TestUtils.defineTablePerson().getName());
|
||||
return updateRequest;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.rdbms.model.metadata;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RDBMSBackendMetaData
|
||||
*******************************************************************************/
|
||||
class RDBMSBackendMetaDataTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Test that an instance can be serialized as expected
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testSerializingToJson()
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
String json = new QInstanceAdapter().qInstanceToJsonIncludingBackend(qInstance);
|
||||
System.out.println(JsonUtils.prettyPrint(json));
|
||||
System.out.println(json);
|
||||
String expectToContain = """
|
||||
"backends":{"default":{"hostName":"mem","password":"","databaseName":"test_database\"""";
|
||||
assertTrue(json.contains(expectToContain));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test that an instance can be deserialized as expected
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testDeserializingFromJson() throws IOException
|
||||
{
|
||||
QInstanceAdapter qInstanceAdapter = new QInstanceAdapter();
|
||||
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
String json = qInstanceAdapter.qInstanceToJsonIncludingBackend(qInstance);
|
||||
|
||||
QInstance deserialized = qInstanceAdapter.jsonToQInstanceIncludingBackends(json);
|
||||
assertThat(deserialized).usingRecursiveComparison().isEqualTo(qInstance);
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +1,28 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
--
|
||||
-- QQQ - Low-code Application Framework for Engineers.
|
||||
-- Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
-- 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
-- contact@kingsrook.com
|
||||
-- https://github.com/Kingsrook/
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU Affero General Public License as
|
||||
-- published by the Free Software Foundation, either version 3 of the
|
||||
-- License, or (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU Affero General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS person;
|
||||
CREATE TABLE person
|
||||
(
|
||||
id SERIAL,
|
||||
id INT AUTO_INCREMENT,
|
||||
create_date TIMESTAMP DEFAULT now(),
|
||||
modify_date TIMESTAMP DEFAULT now(),
|
||||
|
||||
|
Reference in New Issue
Block a user