Skip to content

Commit

Permalink
format insert into expression (#104)
Browse files Browse the repository at this point in the history
* format insert into expression

* 2.14.5
  • Loading branch information
kbarbounakis authored Sep 28, 2024
1 parent 9409f74 commit 37fe140
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 8 deletions.
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@themost/query",
"version": "2.14.4",
"version": "2.14.5",
"description": "MOST Web Framework Codename ZeroGravity - Query Module",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
Expand Down
103 changes: 102 additions & 1 deletion spec/QueryExpression.insert.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { QueryEntity, QueryExpression, SqlFormatter } from '../src/index';
import { QueryEntity, QueryExpression, SqlFormatter, QueryField } from '../src/index';
import { MemoryAdapter } from './test/TestMemoryAdapter';
import { executeInTransactionAsync } from './utils';

describe('SqlFormatter', () => {

Expand Down Expand Up @@ -63,4 +64,104 @@ describe('SqlFormatter', () => {
expect(sql).toEqual('INSERT INTO Table1(id, name, model, price) VALUES (1451, \'New Product\', \'NP800\', 505.5)');
});

it('should use insert into with select', async () => {
await executeInTransactionAsync(db, async () => {
// create table
await db.migrateAsync({
appliesTo: 'OrderStatistics',
version: '1.0.0',
add: [
{
"name": "id",
"type": "Counter",
"primary": true
},
{
"name": "orderedItem",
"type": "Integer",
"nullable": false
},
{
"name": "total",
"type": "Integer",
"nullable": false
}
]
});
// use select insert into statement
const q = new QueryExpression().insert(
new QueryExpression()
.select(
new QueryField('orderedItem'),
new QueryField().count('id').as('total')
)
.from('OrderData')
.groupBy(
new QueryField('orderedItem')
)
).into('OrderStatistics');
await db.executeAsync(q);
const items= await db.executeAsync(new QueryExpression().select(
'orderedItem',
'total'
).from('OrderStatistics'));
expect(items).toBeTruthy();
expect(items.length).toBeTruthy();
})
});

it('should use insert into with select #2', async () => {
await executeInTransactionAsync(db, async () => {
// create table
await db.migrateAsync({
appliesTo: 'OrderStatistics',
version: '1.0.0',
add: [
{
"name": "id",
"type": "Counter",
"primary": true
},
{
"name": "orderedItem",
"type": "Integer",
"nullable": false
},
{
"name": "total",
"type": "Integer",
"nullable": false
}
]
});
// use select insert into statement
const q = new QueryExpression().insert(
new QueryExpression()
.select(
{
"orderedItem": "orderedItem",
},
{
"total": {
"$count": [
"id"
]
}
}
)
.from('OrderData')
.groupBy(
new QueryField('orderedItem')
)
).into('OrderStatistics');
await db.executeAsync(q);
const items= await db.executeAsync(new QueryExpression().select(
'orderedItem',
'total'
).from('OrderStatistics'));
expect(items).toBeTruthy();
expect(items.length).toBeTruthy();
})
});

});
39 changes: 39 additions & 0 deletions spec/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export class CancelTransactionError extends Error {
constructor() {
super();
}
}
/**
*
* @param {import('@themost/common').DataAdapterBase} db
* @param {function():Promise<void>} func
* @returns
*/
export function executeInTransactionAsync(db, func) {
return new Promise((resolve, reject) => {
// start transaction
db.executeInTransaction((cb) => {
try {
func().then(() => {
return cb(new CancelTransactionError());
}).catch( err => {
return cb(err);
});
}
catch (err) {
return cb(err);
}

}, err => {
// if error is an instance of CancelTransactionError
if (err && err instanceof CancelTransactionError) {
return resolve();
}
if (err) {
return reject(err);
}
// exit
return resolve();
});
});
}
1 change: 1 addition & 0 deletions src/formatter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export declare class SqlFormatter {
formatOrder(obj: any): any;
formatGroupBy(obj: any): any;
formatInsert(query: QueryExpression | any): any;
protected formatInsertInto(query: QueryExpression): any;
formatUpdate(query: QueryExpression | any): any;
formatDelete(query: QueryExpression | any): any;
escapeName(name: string): any;
Expand Down
63 changes: 62 additions & 1 deletion src/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,12 +953,16 @@ class SqlFormatter {
*/
formatInsert(obj) {
let self = this, sql = '';
if (isNil(obj.$insert))
if (obj.$insert == null){
throw new Error('Insert expression cannot be empty at this context.');
}
//get entity name
let entity = Object.key(obj.$insert);
//get entity fields
let obj1 = obj.$insert[entity];
if (obj1 instanceof QueryExpression) {
return self.formatInsertInto(obj);
}
let props = [];
for (let prop in obj1)
if (Object.prototype.hasOwnProperty.call(obj1, prop))
Expand All @@ -970,6 +974,63 @@ class SqlFormatter {
}).join(', '), ')');
return sql;
}

/**
* Formats an insert into table(field1, field2, ..)) select from query to the equivalent SQL statement
* @param {QueryExpression} expr
* @returns {string}
*/
formatInsertInto(expr) {
if (expr.$insert == null) {
throw new Error('Insert expression cannot be empty at this context.');
}
//get entity name
const entity = Object.key(expr.$insert);
const insertExpr = expr.$insert[entity];
if (insertExpr instanceof QueryExpression === false) {
throw new Error('Invalid insert expression. Expected a valid query expression.')
}
const select = insertExpr.$select;
if (select == null) {
throw new Error('Invalid insert expression. Expected a valid select expression.');
}
let sql = 'INSERT INTO ' + this.escapeName(entity);
// get fields
let fields = [];
const FormatterCtor = Object.getPrototypeOf(this).constructor;
/**
* @type {SqlFormatter}
*/
const formatter = new FormatterCtor();
for (var key in select) {
if (Object.prototype.hasOwnProperty.call(select, key)) {
var selectFields = select[key];
fields = selectFields.map(function(selectField) {
let name;
if (selectField instanceof QueryField) {
name = selectField.as() || selectField.getName();
} else {
var field = new QueryField(selectField);
name = field.as() || field.getName();
}
if (name == null) {
throw new Error('Invalid select field. Expected a valid field name.');
}
const qualified = name.split('.');
return formatter.escapeName(qualified[qualified.length - 1]);
});
break;
}
}
if (fields.length === 0) {
throw new Error('Invalid insert into expression. Fields cannot be empty.');
}
sql += ' ';
sql += '(' + fields.join(', ') + ')';
sql += ' ' + formatter.format(insertExpr);
return sql;
}

/**
* Formats an update query to the equivalent SQL statement
* @param obj {QueryExpression|*}
Expand Down

0 comments on commit 37fe140

Please sign in to comment.