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');
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', ->