diff --git a/packages/cubejs-bigquery-driver/driver/BigQueryDriver.js b/packages/cubejs-bigquery-driver/driver/BigQueryDriver.js index 29d65e54c653e..4f15b994a6797 100644 --- a/packages/cubejs-bigquery-driver/driver/BigQueryDriver.js +++ b/packages/cubejs-bigquery-driver/driver/BigQueryDriver.js @@ -176,6 +176,16 @@ class BigQueryDriver extends BaseDriver { await pause(5000); } } + + quoteIdentifier(identifier) { + const nestedFields = identifier.split('.'); + return nestedFields.map(name => { + if (name.match(/^[a-z0-9_]+$/)) { + return name; + } + return `\`${identifier}\``; + }).join('.'); + } } module.exports = BigQueryDriver; diff --git a/packages/cubejs-schema-compiler/scaffolding/ScaffoldingTemplate.js b/packages/cubejs-schema-compiler/scaffolding/ScaffoldingTemplate.js index b364312a6c89d..8532ea63f275c 100644 --- a/packages/cubejs-schema-compiler/scaffolding/ScaffoldingTemplate.js +++ b/packages/cubejs-schema-compiler/scaffolding/ScaffoldingTemplate.js @@ -10,12 +10,16 @@ class ScaffoldingTemplate { } escapeName(name) { - if (name.match(/^[a-z0-9_]+$/)) { + if (this.eligibleIdentifier(name)) { return name; } return this.driver.quoteIdentifier(name); } + eligibleIdentifier(name) { + return !!name.match(/^[a-z0-9_]+$/); + } + generateFilesByTableNames(tableNames) { const schemaForTables = this.scaffoldingSchema.generateForTables(tableNames.map(n => this.resolveTableName(n))); return schemaForTables.map(tableSchema => ({ @@ -57,7 +61,7 @@ class ScaffoldingTemplate { })).reduce((a, b) => ({ ...a, ...b }), {}), measures: tableSchema.measures.map(m => ({ [this.memberName(m)]: { - sql: `${this.escapeName(m.name) !== m.name ? '${CUBE}.' : ''}${this.escapeName(m.name)}`, + sql: this.sqlForMember(m), type: m.types[0], title: this.memberTitle(m) } @@ -69,13 +73,18 @@ class ScaffoldingTemplate { }), dimensions: tableSchema.dimensions.map(m => ({ [this.memberName(m)]: { - sql: `${this.escapeName(m.name) !== m.name ? '${CUBE}.' : ''}${this.escapeName(m.name)}`, + sql: this.sqlForMember(m), type: m.types[0], title: this.memberTitle(m), primaryKey: m.isPrimaryKey ? true : undefined } })).reduce((a, b) => ({ ...a, ...b }), {}) - } + }; + } + + sqlForMember(m) { + // eslint-disable-next-line no-template-curly-in-string + return `${this.escapeName(m.name) !== m.name || !this.eligibleIdentifier(m.name) ? '${CUBE}.' : ''}${this.escapeName(m.name)}`; } memberTitle(m) { diff --git a/packages/cubejs-schema-compiler/test/ScaffoldingSchemaTest.js b/packages/cubejs-schema-compiler/test/ScaffoldingSchemaTest.js index 26af8e4825e2c..6b3b52bc422c0 100644 --- a/packages/cubejs-schema-compiler/test/ScaffoldingSchemaTest.js +++ b/packages/cubejs-schema-compiler/test/ScaffoldingSchemaTest.js @@ -1,3 +1,5 @@ +/* eslint-disable quote-props */ +/* globals it,describe */ const ScaffoldingSchema = require('../scaffolding/ScaffoldingSchema'); const ScaffoldingTemplate = require('../scaffolding/ScaffoldingTemplate'); require('should'); @@ -10,6 +12,18 @@ const mySqlDriver = { quoteIdentifier: (name) => `\`${name}\`` }; +const bigQueryDriver = { + quoteIdentifier(identifier) { + const nestedFields = identifier.split('.'); + return nestedFields.map(name => { + if (name.match(/^[a-z0-9_]+$/)) { + return name; + } + return `\`${identifier}\``; + }).join('.'); + } +}; + describe('ScaffoldingSchema', () => { it('schema', () => { const schema = new ScaffoldingSchema({ @@ -461,6 +475,57 @@ describe('ScaffoldingSchema', () => { } } }); +` + } + ]); + }); + + it('big query nested fields', () => { + const template = new ScaffoldingTemplate({ + public: { + orders: [{ + "name": "id", + "type": "integer", + "attributes": [] + }, { + "name": "some.dimension.inside", + "nestedName": ['some', 'dimension', 'inside'], + "type": "string", + "attributes": [] + }] + } + }, bigQueryDriver); + template.generateFilesByTableNames(['public.orders']).should.be.deepEqual([ + { + fileName: 'Orders.js', + content: `cube(\`Orders\`, { + sql: \`SELECT * FROM public.orders\`, + + joins: { + + }, + + measures: { + count: { + type: \`count\`, + drillMembers: [id, someDimensionInside] + } + }, + + dimensions: { + id: { + sql: \`id\`, + type: \`number\`, + primaryKey: true + }, + + someDimensionInside: { + sql: \`\${CUBE}.some.dimension.inside\`, + type: \`string\`, + title: \`Some.dimension.inside\` + } + } +}); ` } ]);