mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-04-25 11:23:44 +03:00
Major bug fix so that the users' privileges enumeration now works properly also on both MySQL < 5.0 and MySQL >= 5.0 also if the user has provided one or more users with -U option;
This commit is contained in:
parent
91a47246f8
commit
09ca578ca1
|
@ -3,7 +3,7 @@ sqlmap (0.6.2-1) stable; urgency=low
|
||||||
* Major bug fix to correctly dump tables entries when --stop is not
|
* Major bug fix to correctly dump tables entries when --stop is not
|
||||||
specified;
|
specified;
|
||||||
* Major bug fix so that the users' privileges enumeration now works
|
* Major bug fix so that the users' privileges enumeration now works
|
||||||
properly also on MySQL < 5.0;
|
properly also on both MySQL < 5.0 and MySQL >= 5.0;
|
||||||
* Major bug fix when the request is POST to also send the url parameters
|
* Major bug fix when the request is POST to also send the url parameters
|
||||||
if any have been provided;
|
if any have been provided;
|
||||||
* Major improvement to correctly enumerate tables, columns and dump
|
* Major improvement to correctly enumerate tables, columns and dump
|
||||||
|
|
|
@ -33,8 +33,8 @@ class Unescaper:
|
||||||
self.__unescaper = unescapeFunction
|
self.__unescaper = unescapeFunction
|
||||||
|
|
||||||
|
|
||||||
def unescape(self, expression):
|
def unescape(self, expression, quote=True):
|
||||||
return self.__unescaper(expression)
|
return self.__unescaper(expression, quote=quote)
|
||||||
|
|
||||||
|
|
||||||
unescaper = Unescaper()
|
unescaper = Unescaper()
|
||||||
|
|
|
@ -67,7 +67,8 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unescape(expression):
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
while True:
|
while True:
|
||||||
index = expression.find("'")
|
index = expression.find("'")
|
||||||
if index == -1:
|
if index == -1:
|
||||||
|
@ -81,16 +82,18 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
lastIndex = firstIndex + index
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
#unescaped = ""
|
#unescaped = "("
|
||||||
unescaped = "("
|
unescaped = ""
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
for i in range(firstIndex, lastIndex):
|
||||||
unescaped += "CHAR(%d)" % (ord(expression[i]))
|
unescaped += "CHAR(%d)" % (ord(expression[i]))
|
||||||
if i < lastIndex - 1:
|
if i < lastIndex - 1:
|
||||||
unescaped += "+"
|
unescaped += "+"
|
||||||
|
|
||||||
unescaped += ")"
|
#unescaped += ")"
|
||||||
expression = expression.replace(old, unescaped)
|
expression = expression.replace(old, unescaped)
|
||||||
|
else:
|
||||||
|
expression = "+".join("CHAR(%d)" % ord(c) for c in expression)
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,8 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unescape(expression):
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
while True:
|
while True:
|
||||||
index = expression.find("'")
|
index = expression.find("'")
|
||||||
if index == -1:
|
if index == -1:
|
||||||
|
@ -88,6 +89,12 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
unescaped += ","
|
unescaped += ","
|
||||||
|
|
||||||
expression = expression.replace(old, "CHAR(%s)" % unescaped)
|
expression = expression.replace(old, "CHAR(%s)" % unescaped)
|
||||||
|
else:
|
||||||
|
unescaped = "CHAR("
|
||||||
|
unescaped += ",".join("%d" % ord(c) for c in expression)
|
||||||
|
unescaped += ")"
|
||||||
|
|
||||||
|
expression = unescaped
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,8 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unescape(expression):
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
while True:
|
while True:
|
||||||
index = expression.find("'")
|
index = expression.find("'")
|
||||||
if index == -1:
|
if index == -1:
|
||||||
|
@ -73,16 +74,18 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
lastIndex = firstIndex + index
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
#unescaped = ""
|
#unescaped = "("
|
||||||
unescaped = "("
|
unescaped = ""
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
for i in range(firstIndex, lastIndex):
|
||||||
unescaped += "CHR(%d)" % (ord(expression[i]))
|
unescaped += "CHR(%d)" % (ord(expression[i]))
|
||||||
if i < lastIndex - 1:
|
if i < lastIndex - 1:
|
||||||
unescaped += "||"
|
unescaped += "||"
|
||||||
|
|
||||||
unescaped += ")"
|
#unescaped += ")"
|
||||||
expression = expression.replace(old, unescaped)
|
expression = expression.replace(old, unescaped)
|
||||||
|
else:
|
||||||
|
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,8 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unescape(expression):
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
while True:
|
while True:
|
||||||
index = expression.find("'")
|
index = expression.find("'")
|
||||||
if index == -1:
|
if index == -1:
|
||||||
|
@ -73,15 +74,18 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
lastIndex = firstIndex + index
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
unescaped = "("
|
#unescaped = "("
|
||||||
|
unescaped = ""
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
for i in range(firstIndex, lastIndex):
|
||||||
unescaped += "CHR(%d)" % (ord(expression[i]))
|
unescaped += "CHR(%d)" % (ord(expression[i]))
|
||||||
if i < lastIndex - 1:
|
if i < lastIndex - 1:
|
||||||
unescaped += "||"
|
unescaped += "||"
|
||||||
|
|
||||||
unescaped += ")"
|
#unescaped += ")"
|
||||||
expression = expression.replace(old, unescaped)
|
expression = expression.replace(old, unescaped)
|
||||||
|
else:
|
||||||
|
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ from lib.core.exception import sqlmapNoneDataException
|
||||||
from lib.core.exception import sqlmapUndefinedMethod
|
from lib.core.exception import sqlmapUndefinedMethod
|
||||||
from lib.core.exception import sqlmapUnsupportedFeatureException
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
from lib.core.shell import autoCompletion
|
from lib.core.shell import autoCompletion
|
||||||
|
from lib.core.unescaper import unescaper
|
||||||
from lib.request import inject
|
from lib.request import inject
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
|
@ -346,19 +347,19 @@ class Enumeration:
|
||||||
if "," in conf.user:
|
if "," in conf.user:
|
||||||
users = conf.user.split(",")
|
users = conf.user.split(",")
|
||||||
query += " WHERE "
|
query += " WHERE "
|
||||||
# NOTE: we need this here only for MySQL 5.0 because
|
# NOTE: I assume that the user provided is not in
|
||||||
# of a known issue explained in queries.xml
|
# MySQL >= 5.0 syntax 'user'@'host'
|
||||||
if kb.dbms == "MySQL" and self.has_information_schema:
|
if kb.dbms == "MySQL" and self.has_information_schema:
|
||||||
likeUser = "%" + conf.user + "%"
|
queryUser = "%" + conf.user + "%"
|
||||||
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
|
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
|
||||||
else:
|
else:
|
||||||
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
|
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
|
||||||
else:
|
else:
|
||||||
# NOTE: we need this here only for MySQL 5.0 because
|
# NOTE: I assume that the user provided is not in
|
||||||
# of a known issue explained in queries.xml
|
# MySQL >= 5.0 syntax 'user'@'host'
|
||||||
if kb.dbms == "MySQL" and self.has_information_schema:
|
if kb.dbms == "MySQL" and self.has_information_schema:
|
||||||
likeUser = "%" + conf.user + "%"
|
queryUser = "%" + conf.user + "%"
|
||||||
query += " WHERE %s LIKE '%s'" % (condition, likeUser)
|
query += " WHERE %s LIKE '%s'" % (condition, queryUser)
|
||||||
else:
|
else:
|
||||||
query += " WHERE %s = '%s'" % (condition, conf.user)
|
query += " WHERE %s = '%s'" % (condition, conf.user)
|
||||||
|
|
||||||
|
@ -406,11 +407,25 @@ class Enumeration:
|
||||||
self.cachedUsersPrivileges[user] = list(privileges)
|
self.cachedUsersPrivileges[user] = list(privileges)
|
||||||
|
|
||||||
if not self.cachedUsersPrivileges:
|
if not self.cachedUsersPrivileges:
|
||||||
|
conditionChar = "="
|
||||||
|
|
||||||
if conf.user:
|
if conf.user:
|
||||||
|
if kb.dbms == "MySQL" and self.has_information_schema:
|
||||||
|
conditionChar = " LIKE "
|
||||||
|
|
||||||
if "," in conf.user:
|
if "," in conf.user:
|
||||||
users = conf.user.split(",")
|
users = set()
|
||||||
|
for user in conf.user.split(","):
|
||||||
|
users.add("%" + user + "%")
|
||||||
else:
|
else:
|
||||||
users = [conf.user]
|
users = [ "%" + conf.user + "%" ]
|
||||||
|
|
||||||
|
elif "," in conf.user:
|
||||||
|
users = conf.user.split(",")
|
||||||
|
|
||||||
|
else:
|
||||||
|
users = [ conf.user ]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not len(self.cachedUsers):
|
if not len(self.cachedUsers):
|
||||||
users = self.getUsers()
|
users = self.getUsers()
|
||||||
|
@ -420,11 +435,10 @@ class Enumeration:
|
||||||
retrievedUsers = set()
|
retrievedUsers = set()
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
if kb.dbms == "MySQL":
|
unescapedUser = None
|
||||||
parsedUser = re.search("\047(.*?)\047@'", user)
|
|
||||||
|
|
||||||
if parsedUser:
|
if kb.dbms == "MySQL" and self.has_information_schema:
|
||||||
user = parsedUser.groups()[0].replace("'", "")
|
unescapedUser = unescaper.unescape(user, quote=False)
|
||||||
|
|
||||||
if user in retrievedUsers:
|
if user in retrievedUsers:
|
||||||
continue
|
continue
|
||||||
|
@ -433,24 +447,26 @@ class Enumeration:
|
||||||
logMsg += "for user '%s'" % user
|
logMsg += "for user '%s'" % user
|
||||||
logger.info(logMsg)
|
logger.info(logMsg)
|
||||||
|
|
||||||
if kb.dbms == "MySQL" and self.has_information_schema:
|
if unescapedUser:
|
||||||
likeUser = "%" + user + "%"
|
queryUser = unescapedUser
|
||||||
else:
|
else:
|
||||||
likeUser = user
|
queryUser = user
|
||||||
|
|
||||||
if kb.dbms == "MySQL" and not self.has_information_schema:
|
if kb.dbms == "MySQL" and not self.has_information_schema:
|
||||||
query = rootQuery["blind"]["count2"] % likeUser
|
query = rootQuery["blind"]["count2"] % queryUser
|
||||||
|
elif kb.dbms == "MySQL" and self.has_information_schema:
|
||||||
|
query = rootQuery["blind"]["count"] % (conditionChar, queryUser)
|
||||||
else:
|
else:
|
||||||
query = rootQuery["blind"]["count"] % likeUser
|
query = rootQuery["blind"]["count"] % queryUser
|
||||||
count = inject.getValue(query, inband=False)
|
count = inject.getValue(query, inband=False)
|
||||||
|
|
||||||
if not len(count) or count == "0":
|
if not len(count) or count == "0":
|
||||||
warnMsg = "unable to retrieve the number of "
|
warnMsg = "unable to retrieve the number of "
|
||||||
warnMsg += "privileges for user '%s'" % likeUser
|
warnMsg += "privileges for user '%s'" % user
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logMsg = "fetching privileges for user '%s'" % likeUser
|
logMsg = "fetching privileges for user '%s'" % user
|
||||||
logger.info(logMsg)
|
logger.info(logMsg)
|
||||||
|
|
||||||
privileges = set()
|
privileges = set()
|
||||||
|
@ -458,13 +474,15 @@ class Enumeration:
|
||||||
|
|
||||||
for index in indexRange:
|
for index in indexRange:
|
||||||
if kb.dbms == "MySQL" and not self.has_information_schema:
|
if kb.dbms == "MySQL" and not self.has_information_schema:
|
||||||
query = rootQuery["blind"]["query2"] % (likeUser, index)
|
query = rootQuery["blind"]["query2"] % (queryUser, index)
|
||||||
|
elif kb.dbms == "MySQL" and self.has_information_schema:
|
||||||
|
query = rootQuery["blind"]["query"] % (conditionChar, queryUser, index)
|
||||||
else:
|
else:
|
||||||
query = rootQuery["blind"]["query"] % (likeUser, index)
|
query = rootQuery["blind"]["query"] % (queryUser, index)
|
||||||
privilege = inject.getValue(query, inband=False)
|
privilege = inject.getValue(query, inband=False)
|
||||||
|
|
||||||
# In PostgreSQL we return 1 if the privilege
|
# In PostgreSQL we get 1 if the privilege is True,
|
||||||
# if True, otherwise 0
|
# 0 otherwise
|
||||||
if kb.dbms == "PostgreSQL" and ", " in privilege:
|
if kb.dbms == "PostgreSQL" and ", " in privilege:
|
||||||
privilege = privilege.replace(", ", ",")
|
privilege = privilege.replace(", ", ",")
|
||||||
privs = privilege.split(",")
|
privs = privilege.split(",")
|
||||||
|
@ -501,6 +519,12 @@ class Enumeration:
|
||||||
if self.__isAdminFromPrivileges(privileges):
|
if self.__isAdminFromPrivileges(privileges):
|
||||||
areAdmins.add(user)
|
areAdmins.add(user)
|
||||||
|
|
||||||
|
# In MySQL < 5.0 we break the cycle after the first
|
||||||
|
# time we get the user's privileges otherwise we
|
||||||
|
# duplicate the same query
|
||||||
|
if kb.dbms == "MySQL" and not self.has_information_schema:
|
||||||
|
break
|
||||||
|
|
||||||
if privileges:
|
if privileges:
|
||||||
self.cachedUsersPrivileges[user] = list(privileges)
|
self.cachedUsersPrivileges[user] = list(privileges)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
</passwords>
|
</passwords>
|
||||||
<privileges>
|
<privileges>
|
||||||
<inband query="SELECT grantee, privilege_type FROM information_schema.USER_PRIVILEGES" condition="grantee" query2="SELECT user, select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user" condition2="user"/>
|
<inband query="SELECT grantee, privilege_type FROM information_schema.USER_PRIVILEGES" condition="grantee" query2="SELECT user, select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user" condition2="user"/>
|
||||||
<!-- TODO: the LIKE clause only affects MySQL >= 5.0. No way so far to use [...] WHERE grantee='%s' in query and count attributes because the unescaper function will forge it as [...] WHERE grantee="CHAR(114,111,111,116)@CHAR(108,111,99,97,108,104,111,115,116)" -->
|
<blind query="SELECT DISTINCT(privilege_type) FROM information_schema.USER_PRIVILEGES WHERE grantee%s%s LIMIT %d, 1" query2="SELECT select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user WHERE user='%s' LIMIT %d, 1" count="SELECT COUNT(DISTINCT(privilege_type)) FROM information_schema.USER_PRIVILEGES WHERE grantee%s%s" count2="SELECT COUNT(*) FROM mysql.user WHERE user='%s'"/>
|
||||||
<blind query="SELECT DISTINCT(privilege_type) FROM information_schema.USER_PRIVILEGES WHERE grantee LIKE '%s' LIMIT %d, 1" query2="SELECT select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user WHERE user='%s' LIMIT %d, 1" count="SELECT COUNT(DISTINCT(privilege_type)) FROM information_schema.USER_PRIVILEGES WHERE grantee LIKE '%s'" count2="SELECT COUNT(*) FROM mysql.user WHERE user='%s'"/>
|
|
||||||
</privileges>
|
</privileges>
|
||||||
<dbs>
|
<dbs>
|
||||||
<inband query="SELECT schema_name FROM information_schema.SCHEMATA" query2="SELECT db FROM mysql.db"/>
|
<inband query="SELECT schema_name FROM information_schema.SCHEMATA" query2="SELECT db FROM mysql.db"/>
|
||||||
|
@ -177,10 +176,12 @@
|
||||||
<inband query="SELECT name FROM master..sysdatabases"/>
|
<inband query="SELECT name FROM master..sysdatabases"/>
|
||||||
<blind query="SELECT TOP 1 name FROM master..sysdatabases WHERE name NOT IN (SELECT TOP %d name FROM master..sysdatabases)" count="SELECT LTRIM(STR(COUNT(name))) FROM master..sysdatabases"/>
|
<blind query="SELECT TOP 1 name FROM master..sysdatabases WHERE name NOT IN (SELECT TOP %d name FROM master..sysdatabases)" count="SELECT LTRIM(STR(COUNT(name))) FROM master..sysdatabases"/>
|
||||||
</dbs>
|
</dbs>
|
||||||
|
<!-- TODO: condition? -->
|
||||||
<tables>
|
<tables>
|
||||||
<inband query="SELECT name FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
|
<inband query="SELECT name FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
|
||||||
<blind query="SELECT TOP 1 name FROM %s..sysobjects WHERE xtype IN ('u', 'v') AND name NOT IN (SELECT TOP %d name FROM %s..sysobjects WHERE xtype IN ('u', 'v'))" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
|
<blind query="SELECT TOP 1 name FROM %s..sysobjects WHERE xtype IN ('u', 'v') AND name NOT IN (SELECT TOP %d name FROM %s..sysobjects WHERE xtype IN ('u', 'v'))" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
|
||||||
</tables>
|
</tables>
|
||||||
|
<!-- TODO: getRange like Oracle? -->
|
||||||
<columns>
|
<columns>
|
||||||
<inband query="SELECT %s..syscolumns.name, TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'"/>
|
<inband query="SELECT %s..syscolumns.name, TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'"/>
|
||||||
<blind query="SELECT TOP 1 name FROM (SELECT TOP %s name FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')) CTABLE" query2="SELECT TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.name='%s' AND %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')"/>
|
<blind query="SELECT TOP 1 name FROM (SELECT TOP %s name FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')) CTABLE" query2="SELECT TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.name='%s' AND %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')"/>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user