From a0f6651368aa3ee20fa917aacff15a578f86f4d7 Mon Sep 17 00:00:00 2001 From: Diego Nieto Cid Date: Fri, 3 Jun 2016 14:29:59 -0300 Subject: [PATCH 1/2] Add test for FOREIGN KEY constraint. --- .../tests/jasmine/server/serverSpec.coffee | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/meteor-postgres/tests/jasmine/server/serverSpec.coffee b/packages/meteor-postgres/tests/jasmine/server/serverSpec.coffee index b9c6fb6..0df4593 100644 --- a/packages/meteor-postgres/tests/jasmine/server/serverSpec.coffee +++ b/packages/meteor-postgres/tests/jasmine/server/serverSpec.coffee @@ -2,6 +2,19 @@ describe 'SQL.Server', -> tableTestTasks = text: ['$string', '$notnull'] + userid: ['$string'] + + tableTestConstraints = [ + [ + '$foreign' + { + $key: [ 'userid' ] + $ref: + $table: 'test_users' + $cols: [ 'id' ] + } + ] + ] tableTestUsers = username: ['$string', '$notnull'] @@ -20,15 +33,15 @@ describe 'SQL.Server', -> testTasks.dropTable().save() testUsers.dropTable().save() catch e - testTasks.createTable(tableTestTasks) - _(3).times (n) -> testTasks.insert({ id: "#{n+1}", text: "testing#{n + 1}" }).save() - _(5).times (n) -> testTasks.insert({ id: "#{n+1+3}", text: "testing1" }).save() - - testUsers.createTable(tableTestUsers) _(3).times (n) -> testUsers.insert({ id: "#{n*2+1}", username: "eddie#{n + 1}", age: 2 * n }).save() testUsers.insert({ id: "#{n*2+2}", username: "paulo", age: 27 }).save() + + + testTasks.createTable(tableTestTasks, tableTestConstraints) + _(3).times (n) -> testTasks.insert({ id: "#{n+1}", text: "testing#{n + 1}", userid: "#{(n % 6) + 1}" }).save() + _(5).times (n) -> testTasks.insert({ id: "#{n+1+3}", text: "testing1" }).save() done() describe 'exceptions', -> @@ -166,6 +179,10 @@ describe 'SQL.Server', -> expect(result.command).toBe('UPDATE') done() + it 'returns an error if foreign key constraint is violated', -> + result = testTasks.insert({id: "100", text: "Posted by unkown", userid: "1904"}).save() + expect(result).toEqual(jasmine.any(Error)) + describe 'update', -> it 'updates correctly with single argument', -> From aa428ce6f9349ed51e2d0a74145a0b9d7cb5ea92 Mon Sep 17 00:00:00 2001 From: Diego Nieto Cid Date: Fri, 3 Jun 2016 14:31:13 -0300 Subject: [PATCH 2/2] Implement server-side foreing key support. --- packages/meteor-postgres/lib/server.coffee | 17 ++++++++++++++- packages/meteor-postgres/lib/sql.coffee | 24 ++++++++++++++++++++++ packages/meteor-postgres/package.js | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/meteor-postgres/lib/server.coffee b/packages/meteor-postgres/lib/server.coffee index 2e75da9..1a5a1c7 100644 --- a/packages/meteor-postgres/lib/server.coffee +++ b/packages/meteor-postgres/lib/server.coffee @@ -37,7 +37,14 @@ _.extend SQL.Server::, SQL.Sql:: # @param tableObj ### -SQL.Server::createTable = (tableObj) -> +SQL.Server::createTable = (tableObj, constraints = []) -> + check constraints, [Match.Where((v) -> + # tuple(String, Object) + check v, Array + check v[0], String + check v[1], Object + v.length == 2)] + startString = "CREATE TABLE IF NOT EXISTS \"#{@table}\" (" item = undefined subKey = undefined @@ -60,6 +67,14 @@ SQL.Server::createTable = (tableObj) -> startString += 'id varchar(255) primary key,' if inputString.indexOf(' id') is -1 + # TableConstraints above are actually column constraints + # as specified by SQL standard. + # The following constraints are real table constraints. + # TODO - rename accordingly + for constraint in constraints + fn = @_Constraints[constraint[0]] + inputString += fn(constraint[1]) + ', ' if fn + watchTrigger = 'watched_table_trigger' @inputString = """ #{startString}#{inputString} created_at TIMESTAMP default now()); diff --git a/packages/meteor-postgres/lib/sql.coffee b/packages/meteor-postgres/lib/sql.coffee index 83f264f..c1bb44a 100644 --- a/packages/meteor-postgres/lib/sql.coffee +++ b/packages/meteor-postgres/lib/sql.coffee @@ -31,6 +31,30 @@ SQL.Sql::_TableConstraints = $default: 'default ' $primary: 'primary key' +###* +# Table Constraints (the ones above are column constraints) +# @type {{$foreign: function}} +# @private +### + +SQL.Sql::_Constraints = + $foreign: (opts) -> + check opts, + $key: [String] + $ref: + $table: String + $cols: Match.Optional([String]) + + quote = (v) -> "\"#{v.replace('"', '\\"')}\"" + sql = "FOREIGN KEY (" + sql += _.map(opts.$key, quote).join(', ') + sql += ") REFERENCES #{quote opts.$ref.$table}" + if opts.$ref.$cols + sql += " (" + sql += _.map(opts.$ref.$cols, quote).join(', ') + sql += ")" + sql + ###* # Notes: Deletes cascade # SQL: DROP TABLE diff --git a/packages/meteor-postgres/package.js b/packages/meteor-postgres/package.js index ce79267..d38c40a 100644 --- a/packages/meteor-postgres/package.js +++ b/packages/meteor-postgres/package.js @@ -14,6 +14,7 @@ Package.onUse(function (api) { api.versionsFrom('1.1.0.2'); api.use('coffeescript'); api.use('underscore'); + api.use('check'); api.use('tracker'); api.use('ddp'); api.use('agershun:alasql@0.2.0');