Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PostgreSQL database driver #32

Open
wants to merge 29 commits into
base: master
from
Open
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2c51b41
Add PostgreSQL DBI extension
peace-maker Jun 7, 2014
6a98add
Add pgsql support to clientprefs
peace-maker Jun 7, 2014
f9ebede
Added pgsql support to sql-admin-manager
peace-maker Jun 7, 2014
de92640
Add --pgsql-path to ambuild configure
peace-maker Jun 7, 2014
e2d31d3
More ambuild fixes
peace-maker Jun 8, 2014
03daa87
Compile libpq with ambuild
peace-maker Jul 3, 2014
1d1a6ba
Try to generate postgres configs
peace-maker Jul 3, 2014
77bfcc3
Add dummy config file
peace-maker Jul 3, 2014
3e493af
More ambuild fixes
peace-maker Jul 3, 2014
8051f5f
Get errorcode and readable error in preparequery
peace-maker Jul 25, 2014
b0de0ee
Fix indentation in AMBuilderPGSQL
peace-maker Jul 31, 2014
6f53f6f
Try to patch and configure postgres through ambuild
peace-maker Aug 3, 2014
1700858
Revert "Try to patch and configure postgres through ambuild"
peace-maker Dec 20, 2014
f6bf491
Update to use PostgreSQL 9.4
peace-maker Dec 20, 2014
6b1262f
Move postgres preparation into seperate script
peace-maker Dec 20, 2014
0fa1652
Add patch to configure.in again
peace-maker Dec 20, 2014
c8578fc
Nit: Support older objdirs for hasPgSql ambuild option
peace-maker Dec 20, 2014
0bf513d
Update to use PostgreSQL 9.4.6
peace-maker Feb 16, 2016
3e2c81d
Use newer AMTL and PGSQL 9.6
peace-maker Oct 20, 2016
b21bb75
Fix threaded queries
peace-maker Oct 28, 2016
9d9ecb9
Fix SetCharacterSet racing with threaded queries
peace-maker Oct 28, 2016
7d5cac5
Use ke::AutoLock for lastInsertID mutex
peace-maker Oct 29, 2016
ac2ac68
Fix fetching one more row than available
peace-maker Nov 3, 2016
ded4711
Improve sanity checks on column access
peace-maker Nov 3, 2016
822529e
Let QuoteString return false if quoting failed
peace-maker May 25, 2018
675809f
Fix build for x64 support changes and update to PGSQL 9.6.9
peace-maker May 25, 2018
1294255
Fix linux build
peace-maker May 26, 2018
54e89fb
Fix building of connection options string
peace-maker Sep 11, 2019
12eb6c6
Update to PostgreSQL 9.6.15
peace-maker Sep 11, 2019
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -110,6 +110,7 @@ class SMConfig(object):
self.generated_headers = None
self.mms_root = None
self.mysql_root = {}
self.pgsql_root = None
self.spcomp = None
self.spcomp_bins = None
self.smx_files = {}
@@ -203,6 +204,18 @@ class SMConfig(object):
raise Exception('Could not find a path to 64-bit MySQL!')
self.mysql_root['x64'] = Normalize(self.mysql_root['x64'])

if getattr(builder.options, 'hasPgSql', False):
if builder.options.pgsql_path:
self.pgsql_root = builder.options.pgsql_path
else:
for i in range(10):
self.pgsql_root = ResolveEnvPath('PGSQL9', 'postgresql-9.' + str(i))
if self.pgsql_root:
break
if not self.pgsql_root or not os.path.isdir(self.pgsql_root):
raise Exception('Could not find a path to PostgreSQL!')
self.pgsql_root = Normalize(self.pgsql_root)

def configure(self):
builder.AddConfigureFile('pushbuild.txt')

@@ -650,6 +663,7 @@ BuildScripts = [
'extensions/cstrike/AMBuilder',
'extensions/geoip/AMBuilder',
'extensions/mysql/AMBuilder',
'extensions/pgsql/AMBuilder',
'extensions/regex/AMBuilder',
'extensions/sdkhooks/AMBuilder',
'extensions/sdktools/AMBuilder',
@@ -0,0 +1,41 @@
CREATE TABLE IF NOT EXISTS sm_cookies
(
id serial,
name varchar(30) NOT NULL UNIQUE,
description varchar(255),
access INTEGER,
PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS sm_cookie_cache
(
player varchar(65) NOT NULL,
cookie_id int NOT NULL,
value varchar(100),
timestamp int NOT NULL,
PRIMARY KEY (player, cookie_id)
);

CREATE LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION add_or_update_cookie(in_player VARCHAR(65), in_cookie INT, in_value VARCHAR(100), in_time INT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the it.
UPDATE sm_cookie_cache SET value = in_value, timestamp = in_time WHERE player = in_player AND cookie_id = in_cookie;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert.
-- if someone else inserts the same key concurrently, we could get a unique-key failure.
BEGIN
INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES (in_player, in_cookie, in_value, in_time);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- do nothing... loop again, and we'll update.
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
@@ -0,0 +1,65 @@
CREATE TABLE sm_admins (
id serial,
authtype varchar(6) NOT NULL,
CHECK (authtype in ('steam', 'name', 'ip')),
identity varchar(65) NOT NULL,
password varchar(65),
flags varchar(30) NOT NULL,
name varchar(65) NOT NULL,
immunity int NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE sm_groups (
id serial,
flags varchar(30) NOT NULL,
name varchar(120) NOT NULL,
immunity_level int NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE sm_group_immunity (
group_id int NOT NULL,
other_id int NOT NULL,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
FOREIGN KEY (other_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
PRIMARY KEY (group_id, other_id)
);

CREATE TABLE sm_group_overrides (
group_id int NOT NULL,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
type varchar(10) NOT NULL,
CHECK (type in ('command', 'group')),
name varchar(32) NOT NULL,
access varchar(5) NOT NULL,
CHECK (access in ('allow', 'deny')),
PRIMARY KEY (group_id, type, name)
);

CREATE TABLE sm_overrides (
type varchar(10) NOT NULL,
CHECK (type in ('command', 'group')),
name varchar(32) NOT NULL,
flags varchar(30) NOT NULL,
PRIMARY KEY (type,name)
);

CREATE TABLE sm_admins_groups (
admin_id int NOT NULL,
group_id int NOT NULL,
FOREIGN KEY (admin_id) REFERENCES sm_admins(id) ON DELETE CASCADE,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
inherit_order int NOT NULL,
PRIMARY KEY (admin_id, group_id)
);

-- side note, this is pgsql module, sm_config will not exist if the above stuff exists... and it's being left to the admin
-- to figure out if it exists.
CREATE TABLE sm_config (
cfg_key varchar(32) NOT NULL,
cfg_value varchar(255) NOT NULL,
PRIMARY KEY (cfg_key)
);

INSERT INTO sm_config (cfg_key, cfg_value) VALUES ('admin_version', '1.0.0.1409');
@@ -24,6 +24,8 @@ def make_objdir_name(p):
help='Path to MySQL 5')
parser.options.add_option('--mysql64-path', type=str, dest='mysql64_path', default=None,
help='Path to 64-bit MySQL 5')
parser.options.add_option('--pgsql-path', type=str, dest='pgsql_path', default=None,
help='Path to PostgreSQL 9')
parser.options.add_option('--mms-path', type=str, dest='mms_path', default=None,
help='Path to Metamod:Source')
parser.options.add_option('--enable-debug', action='store_const', const='1', dest='debug',
@@ -32,6 +34,8 @@ def make_objdir_name(p):
help='Enable optimization')
parser.options.add_option('--no-mysql', action='store_false', default=True, dest='hasMySql',
help='Disable building MySQL extension')
parser.options.add_option('--no-pgsql', action='store_false', default=True, dest='hasPgSql',
help='Disable building PostgreSQL extension')
parser.options.add_option('-s', '--sdks', default='all', dest='sdks',
help='Build against specified SDKs; valid args are "all", "present", or '
'comma-delimited list of engine names (default: %default)')
@@ -287,6 +287,74 @@ void ClientPrefs::DatabaseConnect()
goto fatal_fail;
}
}
else if (strcmp(identifier, "pgsql") == 0)
{
g_DriverType = Driver_PgSQL;
// PostgreSQL supports 'IF NOT EXISTS' as of 9.1
if (!Database->DoSimpleQuery(
"CREATE TABLE IF NOT EXISTS sm_cookies \
( \
id serial, \
name varchar(30) NOT NULL UNIQUE, \
description varchar(255), \
access INTEGER, \
PRIMARY KEY (id) \
)"))
{
g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookies: %s", Database->GetError());
goto fatal_fail;
}

if (!Database->DoSimpleQuery(
"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
( \
player varchar(65) NOT NULL, \
cookie_id int NOT NULL, \
value varchar(100), \
timestamp int NOT NULL, \
PRIMARY KEY (player, cookie_id) \
)"))
{
g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
goto fatal_fail;
}

if (!Database->DoSimpleQuery(
"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
( \
player varchar(65) NOT NULL, \
cookie_id int NOT NULL, \
value varchar(100), \
timestamp int NOT NULL, \
PRIMARY KEY (player, cookie_id) \
)"))
{
g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
goto fatal_fail;
}

if (!Database->DoSimpleQuery(
"CREATE OR REPLACE FUNCTION add_or_update_cookie(in_player VARCHAR(65), in_cookie INT, in_value VARCHAR(100), in_time INT) RETURNS VOID AS \
$$ \
BEGIN \
LOOP \
UPDATE sm_cookie_cache SET value = in_value, timestamp = in_time WHERE player = in_player AND cookie_id = in_cookie; \
IF found THEN \
RETURN; \
END IF; \
BEGIN \
INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES (in_player, in_cookie, in_value, in_time); \
RETURN; \
EXCEPTION WHEN unique_violation THEN \
END; \
END LOOP; \
END; \
$$ LANGUAGE plpgsql;"))
{
g_pSM->LogMessage(myself, "Failed to create function add_or_update_cookie: %s", Database->GetError());
goto fatal_fail;
}
}
else
{
g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
@@ -49,7 +49,8 @@ char * UTIL_strncpy(char * destination, const char * source, size_t num);
enum DbDriver
{
Driver_MySQL,
Driver_SQLite
Driver_SQLite,
Driver_PgSQL
};

#define MAX_TRANSLATE_PARAMS 32
@@ -177,6 +177,17 @@ bool TQueryOp::BindParamsAndRun()
safe_desc,
m_params.cookie->access);
}
else if (g_DriverType == Driver_PgSQL)
{
// just insert. Returns error on already exists, so ignore the error.
g_pSM->Format(query,
sizeof(query),
"INSERT INTO sm_cookies (name, description, access) \
VALUES ('%s', '%s', %d)",
safe_name,
safe_desc,
m_params.cookie->access);
}

if (!m_database->DoSimpleQuery(query))
{
@@ -250,6 +261,18 @@ bool TQueryOp::BindParamsAndRun()
safe_val,
(unsigned int)m_params.data->timestamp);
}
else if (g_DriverType == Driver_PgSQL)
{
// Using a PL/Pgsql function, called add_or_update_cookie(),
// since Postgres does not have an 'OR REPLACE' functionality.
g_pSM->Format(query,
sizeof(query),
"SELECT add_or_update_cookie ('%s', %d, '%s', %d)",
safe_id,
m_params.cookieId,
safe_val,
(unsigned int)m_params.data->timestamp);
}

if (!m_database->DoSimpleQuery(query))
{
@@ -0,0 +1,40 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os

if SM.pgsql_root:
# First compile libpq
libpq = builder.Build('AMBuilderPGSQL', { 'SM': SM })

binary = SM.ExtLibrary(builder, 'dbi.pgsql.ext', 'x86')
binary.compiler.cxxincludes += [
os.path.join(SM.pgsql_root, 'src', 'interfaces', 'libpq'),
os.path.join(SM.pgsql_root, 'src', 'include'),
os.path.join(SM.mms_root, 'core', 'sourcehook')
]

# Link the compiled libpq
binary.compiler.postlink += [libpq.binary]

if builder.target.platform == 'linux' or builder.target.platform == 'mac':
binary.compiler.postlink += [
'-lz',
'-lpthread',
'-lm'
]
elif builder.target.platform == 'windows':
binary.compiler.postlink += [
'wsock32.lib',
'ws2_32.lib',
'secur32.lib'
]

binary.sources += [
os.path.join('..', '..', 'public', 'smsdk_ext.cpp'),
os.path.join('pgsql', 'PgBasicResults.cpp'),
os.path.join('pgsql', 'PgDatabase.cpp'),
os.path.join('pgsql', 'PgDriver.cpp'),
os.path.join('pgsql', 'PgStatement.cpp'),
'extension.cpp'
]
SM.extensions += [builder.Add(binary)]

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.